Authored by DreamPiggy

Decode the image in the operation level's queue instead of URLSession delegate q…

…ueue. Because URLSession delegate queue is a barrier queue and shared between different operations
@@ -38,7 +38,9 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary; @@ -38,7 +38,9 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
38 38
39 @property (strong, nonatomic, readwrite, nullable) NSURLSessionTask *dataTask; 39 @property (strong, nonatomic, readwrite, nullable) NSURLSessionTask *dataTask;
40 40
41 -@property (strong, nonatomic, nullable) dispatch_queue_t barrierQueue; 41 +@property (strong, nonatomic, nonnull) dispatch_queue_t barrierQueue;
  42 +
  43 +@property (strong, nonatomic, nonnull) dispatch_queue_t coderQueue;
42 44
43 #if SD_UIKIT 45 #if SD_UIKIT
44 @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId; 46 @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
@@ -70,6 +72,7 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary; @@ -70,6 +72,7 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
70 _expectedSize = 0; 72 _expectedSize = 0;
71 _unownedSession = session; 73 _unownedSession = session;
72 _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT); 74 _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
  75 + _coderQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationCoderQueue", DISPATCH_QUEUE_SERIAL);
73 } 76 }
74 return self; 77 return self;
75 } 78 }
@@ -316,7 +319,7 @@ didReceiveResponse:(NSURLResponse *)response @@ -316,7 +319,7 @@ didReceiveResponse:(NSURLResponse *)response
316 319
317 if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) { 320 if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {
318 // Get the image data 321 // Get the image data
319 - NSData *imageData = [self.imageData copy]; 322 + __block NSData *imageData = [self.imageData copy];
320 // Get the total bytes downloaded 323 // Get the total bytes downloaded
321 const NSInteger totalSize = imageData.length; 324 const NSInteger totalSize = imageData.length;
322 // Get the finish status 325 // Get the finish status
@@ -333,16 +336,18 @@ didReceiveResponse:(NSURLResponse *)response @@ -333,16 +336,18 @@ didReceiveResponse:(NSURLResponse *)response
333 } 336 }
334 } 337 }
335 338
  339 + dispatch_async(self.coderQueue, ^{
336 UIImage *image = [self.progressiveCoder incrementallyDecodedImageWithData:imageData finished:finished]; 340 UIImage *image = [self.progressiveCoder incrementallyDecodedImageWithData:imageData finished:finished];
337 if (image) { 341 if (image) {
338 NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; 342 NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
339 image = [self scaledImageForKey:key image:image]; 343 image = [self scaledImageForKey:key image:image];
340 if (self.shouldDecompressImages) { 344 if (self.shouldDecompressImages) {
341 - image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&data options:@{SDWebImageCoderScaleDownLargeImagesKey: @(NO)}]; 345 + image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&imageData options:@{SDWebImageCoderScaleDownLargeImagesKey: @(NO)}];
342 } 346 }
343 347
344 [self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO]; 348 [self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
345 } 349 }
  350 + });
346 } 351 }
347 352
348 for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) { 353 for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
@@ -380,14 +385,16 @@ didReceiveResponse:(NSURLResponse *)response @@ -380,14 +385,16 @@ didReceiveResponse:(NSURLResponse *)response
380 }); 385 });
381 } 386 }
382 387
  388 + // make sure to call `[self done]` to mark operation as finished
383 if (error) { 389 if (error) {
384 [self callCompletionBlocksWithError:error]; 390 [self callCompletionBlocksWithError:error];
  391 + [self done];
385 } else { 392 } else {
386 if ([self callbacksForKey:kCompletedCallbackKey].count > 0) { 393 if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {
387 /** 394 /**
388 * If you specified to use `NSURLCache`, then the response you get here is what you need. 395 * If you specified to use `NSURLCache`, then the response you get here is what you need.
389 */ 396 */
390 - NSData *imageData = [self.imageData copy]; 397 + __block NSData *imageData = [self.imageData copy];
391 if (imageData) { 398 if (imageData) {
392 /** if you specified to only use cached data via `SDWebImageDownloaderIgnoreCachedResponse`, 399 /** if you specified to only use cached data via `SDWebImageDownloaderIgnoreCachedResponse`,
393 * then we should check if the cached data is equal to image data 400 * then we should check if the cached data is equal to image data
@@ -395,7 +402,9 @@ didReceiveResponse:(NSURLResponse *)response @@ -395,7 +402,9 @@ didReceiveResponse:(NSURLResponse *)response
395 if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) { 402 if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) {
396 // call completion block with nil 403 // call completion block with nil
397 [self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES]; 404 [self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES];
  405 + [self done];
398 } else { 406 } else {
  407 + dispatch_async(self.coderQueue, ^{
399 UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:imageData]; 408 UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:imageData];
400 NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; 409 NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
401 image = [self scaledImageForKey:key image:image]; 410 image = [self scaledImageForKey:key image:image];
@@ -425,13 +434,17 @@ didReceiveResponse:(NSURLResponse *)response @@ -425,13 +434,17 @@ didReceiveResponse:(NSURLResponse *)response
425 } else { 434 } else {
426 [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES]; 435 [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES];
427 } 436 }
  437 + [self done];
  438 + });
428 } 439 }
429 } else { 440 } else {
430 [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]]; 441 [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];
  442 + [self done];
431 } 443 }
  444 + } else {
  445 + [self done];
432 } 446 }
433 } 447 }
434 - [self done];  
435 } 448 }
436 449
437 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { 450 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {