diff --git a/SDWebImage/Core/SDWebImageDownloaderOperation.m b/SDWebImage/Core/SDWebImageDownloaderOperation.m index 67d1a8e2..1de8212a 100644 --- a/SDWebImage/Core/SDWebImageDownloaderOperation.m +++ b/SDWebImage/Core/SDWebImageDownloaderOperation.m @@ -54,7 +54,7 @@ typedef NSMutableDictionary SDCallbacksDictionary; @property (strong, nonatomic, readwrite, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); -@property (strong, nonatomic, nonnull) dispatch_queue_t coderQueue; // the queue to do image decoding +@property (strong, nonatomic, nonnull) NSOperationQueue *coderQueue; // the serial operation queue to do image decoding #if SD_UIKIT @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId; #endif @@ -89,7 +89,8 @@ typedef NSMutableDictionary SDCallbacksDictionary; _finished = NO; _expectedSize = 0; _unownedSession = session; - _coderQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationCoderQueue", DISPATCH_QUEUE_SERIAL); + _coderQueue = [NSOperationQueue new]; + _coderQueue.maxConcurrentOperationCount = 1; #if SD_UIKIT _backgroundTaskId = UIBackgroundTaskInvalid; #endif @@ -384,17 +385,18 @@ didReceiveResponse:(NSURLResponse *)response // Get the image data NSData *imageData = [self.imageData copy]; - // progressive decode the image in coder queue - dispatch_async(self.coderQueue, ^{ - @autoreleasepool { + // keep maxmium one progressive decode process during download + if (self.coderQueue.operationCount == 0) { + // NSOperation have autoreleasepool, don't need to create extra one + [self.coderQueue addOperationWithBlock:^{ UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, finished, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context); if (image) { // We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding. [self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO]; } - } - }); + }]; + } } for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) { @@ -461,19 +463,18 @@ didReceiveResponse:(NSURLResponse *)response [self callCompletionBlocksWithError:self.responseError]; [self done]; } else { - // decode the image in coder queue - dispatch_async(self.coderQueue, ^{ - @autoreleasepool { - UIImage *image = SDImageLoaderDecodeImageData(imageData, self.request.URL, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context); - CGSize imageSize = image.size; - if (imageSize.width == 0 || imageSize.height == 0) { - [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]]; - } else { - [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES]; - } - [self done]; + // decode the image in coder queue, cancel all previous decoding process + [self.coderQueue cancelAllOperations]; + [self.coderQueue addOperationWithBlock:^{ + UIImage *image = SDImageLoaderDecodeImageData(imageData, self.request.URL, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context); + CGSize imageSize = image.size; + if (imageSize.width == 0 || imageSize.height == 0) { + [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]]; + } else { + [self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES]; } - }); + [self done]; + }]; } } else { [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];