|
14 | 14 | #import "SDImageCacheDefine.h"
|
15 | 15 | #import "SDCallbackQueue.h"
|
16 | 16 |
|
| 17 | +BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation> operation); // Private currently, mark open if needed |
| 18 | + |
17 | 19 | // A handler to represent individual request
|
18 | 20 | @interface SDWebImageDownloaderOperationToken : NSObject
|
19 | 21 |
|
@@ -110,8 +112,9 @@ - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
|
110 | 112 | _finished = NO;
|
111 | 113 | _expectedSize = 0;
|
112 | 114 | _unownedSession = session;
|
113 |
| - _coderQueue = [NSOperationQueue new]; |
| 115 | + _coderQueue = [[NSOperationQueue alloc] init]; |
114 | 116 | _coderQueue.maxConcurrentOperationCount = 1;
|
| 117 | + _coderQueue.name = @"com.hackemist.SDWebImageDownloaderOperation.coderQueue"; |
115 | 118 | _imageMap = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:1];
|
116 | 119 | #if SD_UIKIT
|
117 | 120 | _backgroundTaskId = UIBackgroundTaskInvalid;
|
@@ -231,13 +234,10 @@ - (void)start {
|
231 | 234 | if (self.dataTask) {
|
232 | 235 | if (self.options & SDWebImageDownloaderHighPriority) {
|
233 | 236 | self.dataTask.priority = NSURLSessionTaskPriorityHigh;
|
234 |
| - self.coderQueue.qualityOfService = NSQualityOfServiceUserInteractive; |
235 | 237 | } else if (self.options & SDWebImageDownloaderLowPriority) {
|
236 | 238 | self.dataTask.priority = NSURLSessionTaskPriorityLow;
|
237 |
| - self.coderQueue.qualityOfService = NSQualityOfServiceBackground; |
238 | 239 | } else {
|
239 | 240 | self.dataTask.priority = NSURLSessionTaskPriorityDefault;
|
240 |
| - self.coderQueue.qualityOfService = NSQualityOfServiceDefault; |
241 | 241 | }
|
242 | 242 | [self.dataTask resume];
|
243 | 243 | NSArray<SDWebImageDownloaderOperationToken *> *tokens;
|
@@ -471,22 +471,22 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data
|
471 | 471 | if (self.coderQueue.operationCount == 0) {
|
472 | 472 | // NSOperation have autoreleasepool, don't need to create extra one
|
473 | 473 | @weakify(self);
|
474 |
| - NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ |
| 474 | + [self.coderQueue addOperationWithBlock:^{ |
475 | 475 | @strongify(self);
|
476 | 476 | if (!self) {
|
477 | 477 | return;
|
478 | 478 | }
|
479 |
| - UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, NO, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context); |
480 |
| - if (self.isFinished) { |
| 479 | + // When cancelled or transfer finished (`didCompleteWithError`), cancel the progress callback, only completed block is called and enough |
| 480 | + if (self.isCancelled || SDWebImageDownloaderOperationGetCompleted(self)) { |
481 | 481 | return;
|
482 | 482 | }
|
| 483 | + UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, NO, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context); |
483 | 484 | if (image) {
|
484 | 485 | // 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.
|
485 | 486 |
|
486 | 487 | [self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
|
487 | 488 | }
|
488 | 489 | }];
|
489 |
| - [self.coderQueue addOperation:operation]; |
490 | 490 | }
|
491 | 491 | }
|
492 | 492 |
|
@@ -564,16 +564,8 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didComp
|
564 | 564 | // decode the image in coder queue, cancel all previous decoding process
|
565 | 565 | [self.coderQueue cancelAllOperations];
|
566 | 566 | @weakify(self);
|
567 |
| - // call done after all different variant completed block was dispatched |
568 |
| - NSOperation *doneOperation = [NSBlockOperation blockOperationWithBlock:^{ |
569 |
| - @strongify(self); |
570 |
| - if (!self) { |
571 |
| - return; |
572 |
| - } |
573 |
| - [self done]; |
574 |
| - }]; |
575 | 567 | for (SDWebImageDownloaderOperationToken *token in tokens) {
|
576 |
| - NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ |
| 568 | + [self.coderQueue addOperationWithBlock:^{ |
577 | 569 | @strongify(self);
|
578 | 570 | if (!self) {
|
579 | 571 | return;
|
@@ -612,11 +604,22 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didComp
|
612 | 604 | [self callCompletionBlockWithToken:token image:image imageData:imageData error:nil finished:YES];
|
613 | 605 | }
|
614 | 606 | }];
|
615 |
| - [doneOperation addDependency:operation]; |
616 |
| - [self.coderQueue addOperation:operation]; |
617 | 607 | }
|
618 | 608 | // call [self done] after all completed block was dispatched
|
619 |
| - [self.coderQueue addOperation:doneOperation]; |
| 609 | + dispatch_block_t doneBlock = ^{ |
| 610 | + @strongify(self); |
| 611 | + if (!self) { |
| 612 | + return; |
| 613 | + } |
| 614 | + [self done]; |
| 615 | + }; |
| 616 | + if (@available(iOS 13, tvOS 13, macOS 10.15, watchOS 6, *)) { |
| 617 | + // seems faster than `addOperationWithBlock` |
| 618 | + [self.coderQueue addBarrierBlock:doneBlock]; |
| 619 | + } else { |
| 620 | + // serial queue, this does the same effect in semantics |
| 621 | + [self.coderQueue addOperationWithBlock:doneBlock]; |
| 622 | + } |
620 | 623 | }
|
621 | 624 | } else {
|
622 | 625 | [self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];
|
|
0 commit comments