Skip to content

Commit 6e1fee7

Browse files
committed
Try not introduce new public API for operation, attach the isCompleted property using associated object from downloader, ensure compatibility
1 parent 43b9413 commit 6e1fee7

File tree

3 files changed

+27
-14
lines changed

3 files changed

+27
-14
lines changed

SDWebImage/Core/SDWebImageDownloader.m

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,30 @@
1313
#import "SDWebImageCacheKeyFilter.h"
1414
#import "SDImageCacheDefine.h"
1515
#import "SDInternalMacros.h"
16+
#import "objc/runtime.h"
1617

1718
NSNotificationName const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification";
1819
NSNotificationName const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDownloadReceiveResponseNotification";
1920
NSNotificationName const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
2021
NSNotificationName const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification";
2122

2223
static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
24+
static void * SDWebImageDownloaderOperationKey = &SDWebImageDownloaderOperationKey;
25+
26+
BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation> operation) {
27+
NSCParameterAssert(operation);
28+
NSNumber *value = objc_getAssociatedObject(operation, SDWebImageDownloaderOperationKey);
29+
if (value) {
30+
return value.boolValue;
31+
} else {
32+
return NO;
33+
}
34+
}
35+
36+
void SDWebImageDownloaderOperationSetCompleted(id<SDWebImageDownloaderOperation> operation, BOOL isCompleted) {
37+
NSCParameterAssert(operation);
38+
objc_setAssociatedObject(operation, SDWebImageDownloaderOperationKey, @(isCompleted), OBJC_ASSOCIATION_RETAIN);
39+
}
2340

2441
@interface SDWebImageDownloadToken ()
2542

@@ -219,7 +236,8 @@ - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
219236
SDImageCoderOptions *decodeOptions = SDGetDecodeOptionsFromContext(context, [self.class imageOptionsFromDownloaderOptions:options], cacheKey);
220237
NSOperation<SDWebImageDownloaderOperation> *operation = [self.URLOperations objectForKey:url];
221238
// There is a case that the operation may be marked as finished or cancelled, but not been removed from `self.URLOperations`.
222-
if (!operation || operation.isFinished || operation.isCancelled || operation.isTransferFinished) {
239+
BOOL shouldNotReuseOperation = !operation || operation.isFinished || operation.isCancelled || SDWebImageDownloaderOperationGetCompleted(operation);
240+
if (shouldNotReuseOperation) {
223241
operation = [self createDownloaderOperationWithUrl:url options:options context:context];
224242
if (!operation) {
225243
SD_UNLOCK(_operationsLock);
@@ -499,6 +517,10 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didComp
499517

500518
// Identify the operation that runs this task and pass it the delegate method
501519
NSOperation<SDWebImageDownloaderOperation> *dataOperation = [self operationWithTask:task];
520+
if (dataOperation) {
521+
// Mark the downloader operation `isCompleted = YES`, no longer re-use this operation when new request comes in
522+
SDWebImageDownloaderOperationSetCompleted(dataOperation, true);
523+
}
502524
if ([dataOperation respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]) {
503525
[dataOperation URLSession:session task:task didCompleteWithError:error];
504526
}

SDWebImage/Core/SDWebImageDownloaderOperation.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535

3636
- (BOOL)cancel:(nullable id)token;
3737

38-
@property (assign, readonly) BOOL isTransferFinished;
39-
4038
@property (strong, nonatomic, readonly, nullable) NSURLRequest *request;
4139
@property (strong, nonatomic, readonly, nullable) NSURLResponse *response;
4240

@@ -58,10 +56,6 @@
5856
*/
5957
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageDownloaderOperation>
6058

61-
/// Whether the operation's network data transfer is finished. This is used by downloader to decide whether to call `addHandlersForProgress:`, or create a new operation instead.
62-
/// @note You must implements this or this will cause downloader attach new handlers for a already finished operation, may cause some callback missing.
63-
@property (assign, readonly) BOOL isTransferFinished;
64-
6559
/**
6660
* The request used by the operation's task.
6761
*/

SDWebImage/Core/SDWebImageDownloaderOperation.m

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#import "SDImageCacheDefine.h"
1515
#import "SDCallbackQueue.h"
1616

17+
BOOL SDWebImageDownloaderOperationGetCompleted(id<SDWebImageDownloaderOperation> operation); // Private currently, mark open if needed
18+
1719
// A handler to represent individual request
1820
@interface SDWebImageDownloaderOperationToken : NSObject
1921

@@ -74,7 +76,6 @@ @interface SDWebImageDownloaderOperation ()
7476
@property (strong, nonatomic, readwrite, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
7577

7678
@property (strong, nonatomic, nonnull) dispatch_queue_t coderQueue; // the serial operation queue to do image decoding
77-
@property (assign, readwrite) BOOL isTransferFinished; // Whether current operation's network transfer is finished (actually, `didCompleteWithError` already been called)
7879

7980
@property (strong, nonatomic, nonnull) NSMapTable<SDImageCoderOptions *, UIImage *> *imageMap; // each variant of image is weak-referenced to avoid too many re-decode during downloading
8081
#if SD_UIKIT
@@ -472,7 +473,7 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data
472473
return;
473474
}
474475
// When cancelled or transfer finished (`didCompleteWithError`), cancel the progress callback, only completed block is called and enough
475-
if (self.isCancelled || self.isTransferFinished) {
476+
if (self.isCancelled || SDWebImageDownloaderOperationGetCompleted(self)) {
476477
return;
477478
}
478479
UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, NO, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context);
@@ -512,7 +513,6 @@ - (void)URLSession:(NSURLSession *)session
512513
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
513514
// If we already cancel the operation or anything mark the operation finished, don't callback twice
514515
if (self.isFinished) return;
515-
self.isTransferFinished = YES;
516516

517517
NSArray<SDWebImageDownloaderOperationToken *> *tokens;
518518
@synchronized (self) {
@@ -599,16 +599,13 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didComp
599599
});
600600
}
601601
// call [self done] after all completed block was dispatched
602-
dispatch_barrier_async(self.coderQueue, ^{
602+
dispatch_async(self.coderQueue, ^{
603603
@strongify(self);
604604
if (!self) {
605605
return;
606606
}
607607
[self done];
608608
});
609-
dispatch_async(self.coderQueue, ^{
610-
[self done];
611-
});
612609
}
613610
} else {
614611
[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorBadImageData userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];

0 commit comments

Comments
 (0)