Authored by DreamPiggy

Change access the imageData in NSURLSession delegate queue to solve non thread-s…

…afe issue. Also, ensure imageData in completion block is immutable to avoid accident modification from the other queue
@@ -228,7 +228,20 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary; @@ -228,7 +228,20 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
228 [weakSelf.callbackBlocks removeAllObjects]; 228 [weakSelf.callbackBlocks removeAllObjects];
229 }); 229 });
230 self.dataTask = nil; 230 self.dataTask = nil;
231 - self.imageData = nil; 231 +
  232 + NSOperationQueue *delegateQueue;
  233 + if (self.unownedSession) {
  234 + delegateQueue = self.unownedSession.delegateQueue;
  235 + } else {
  236 + delegateQueue = self.ownedSession.delegateQueue;
  237 + }
  238 + if (delegateQueue) {
  239 + NSAssert(delegateQueue.maxConcurrentOperationCount == 1, @"NSURLSession delegate queue should be a serial queue");
  240 + [delegateQueue addOperationWithBlock:^{
  241 + weakSelf.imageData = nil;
  242 + }];
  243 + }
  244 +
232 if (self.ownedSession) { 245 if (self.ownedSession) {
233 [self.ownedSession invalidateAndCancel]; 246 [self.ownedSession invalidateAndCancel];
234 self.ownedSession = nil; 247 self.ownedSession = nil;
@@ -427,8 +440,9 @@ didReceiveResponse:(NSURLResponse *)response @@ -427,8 +440,9 @@ didReceiveResponse:(NSURLResponse *)response
427 * the response data will be nil. 440 * the response data will be nil.
428 * So we don't need to check the cache option here, since the system will obey the cache option 441 * So we don't need to check the cache option here, since the system will obey the cache option
429 */ 442 */
430 - if (self.imageData) {  
431 - UIImage *image = [UIImage sd_imageWithData:self.imageData]; 443 + NSData *imageData = [self.imageData copy];
  444 + if (imageData) {
  445 + UIImage *image = [UIImage sd_imageWithData:imageData];
432 NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; 446 NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
433 image = [self scaledImageForKey:key image:image]; 447 image = [self scaledImageForKey:key image:image];
434 448
@@ -438,7 +452,7 @@ didReceiveResponse:(NSURLResponse *)response @@ -438,7 +452,7 @@ didReceiveResponse:(NSURLResponse *)response
438 if (self.options & SDWebImageDownloaderScaleDownLargeImages) { 452 if (self.options & SDWebImageDownloaderScaleDownLargeImages) {
439 #if SD_UIKIT || SD_WATCH 453 #if SD_UIKIT || SD_WATCH
440 image = [UIImage decodedAndScaledDownImageWithImage:image]; 454 image = [UIImage decodedAndScaledDownImageWithImage:image];
441 - [self.imageData setData:UIImagePNGRepresentation(image)]; 455 + imageData = UIImagePNGRepresentation(image);
442 #endif 456 #endif
443 } else { 457 } else {
444 image = [UIImage decodedImageWithImage:image]; 458 image = [UIImage decodedImageWithImage:image];
@@ -448,7 +462,7 @@ didReceiveResponse:(NSURLResponse *)response @@ -448,7 +462,7 @@ didReceiveResponse:(NSURLResponse *)response
448 if (CGSizeEqualToSize(image.size, CGSizeZero)) { 462 if (CGSizeEqualToSize(image.size, CGSizeZero)) {
449 [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]]; 463 [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]];
450 } else { 464 } else {
451 - [self callCompletionBlocksWithImage:image imageData:self.imageData error:nil finished:YES]; 465 + [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES];
452 } 466 }
453 } else { 467 } else {
454 [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]]; 468 [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];