|
@@ -13,8 +13,9 @@ |
|
@@ -13,8 +13,9 @@ |
13
|
@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
|
13
|
@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
|
14
|
|
14
|
|
15
|
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
|
15
|
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
|
16
|
-@property (copy, nonatomic, nullable) SDWebImageNoParamsBlock cancelBlock;
|
16
|
+@property (strong, nonatomic, nullable) SDWebImageDownloadToken *downloadToken;
|
17
|
@property (strong, nonatomic, nullable) NSOperation *cacheOperation;
|
17
|
@property (strong, nonatomic, nullable) NSOperation *cacheOperation;
|
|
|
18
|
+@property (weak, nonatomic, nullable) SDWebImageManager *manager;
|
18
|
|
19
|
|
19
|
@end
|
20
|
@end
|
20
|
|
21
|
|
|
@@ -124,8 +125,8 @@ |
|
@@ -124,8 +125,8 @@ |
124
|
url = nil;
|
125
|
url = nil;
|
125
|
}
|
126
|
}
|
126
|
|
127
|
|
127
|
- __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
|
|
|
128
|
- __weak SDWebImageCombinedOperation *weakOperation = operation;
|
128
|
+ SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
|
|
|
129
|
+ operation.manager = self;
|
129
|
|
130
|
|
130
|
BOOL isFailedUrl = NO;
|
131
|
BOOL isFailedUrl = NO;
|
131
|
if (url) {
|
132
|
if (url) {
|
|
@@ -148,10 +149,12 @@ |
|
@@ -148,10 +149,12 @@ |
148
|
if (options & SDWebImageCacheMemoryOnly) cacheOptions |= SDImageCacheQueryMemoryOnly;
|
149
|
if (options & SDWebImageCacheMemoryOnly) cacheOptions |= SDImageCacheQueryMemoryOnly;
|
149
|
if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
|
150
|
if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
|
150
|
if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
|
151
|
if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
|
151
|
-
|
152
|
+
|
|
|
153
|
+ __weak SDWebImageCombinedOperation *weakOperation = operation;
|
152
|
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
|
154
|
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
|
153
|
- if (operation.isCancelled) {
|
|
|
154
|
- [self safelyRemoveOperationFromRunning:operation];
|
155
|
+ __strong __typeof(weakOperation) strongOperation = weakOperation;
|
|
|
156
|
+ if (!strongOperation || strongOperation.isCancelled) {
|
|
|
157
|
+ [self safelyRemoveOperationFromRunning:strongOperation];
|
155
|
return;
|
158
|
return;
|
156
|
}
|
159
|
}
|
157
|
|
160
|
|
|
@@ -159,7 +162,7 @@ |
|
@@ -159,7 +162,7 @@ |
159
|
if (cachedImage && options & SDWebImageRefreshCached) {
|
162
|
if (cachedImage && options & SDWebImageRefreshCached) {
|
160
|
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
|
163
|
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
|
161
|
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
|
164
|
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
|
162
|
- [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
165
|
+ [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
163
|
}
|
166
|
}
|
164
|
|
167
|
|
165
|
// download if no image or requested to refresh anyway, and download allowed by delegate
|
168
|
// download if no image or requested to refresh anyway, and download allowed by delegate
|
|
@@ -180,14 +183,16 @@ |
|
@@ -180,14 +183,16 @@ |
180
|
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
|
183
|
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
|
181
|
}
|
184
|
}
|
182
|
|
185
|
|
183
|
- SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
|
|
|
184
|
- __strong __typeof(weakOperation) strongOperation = weakOperation;
|
|
|
185
|
- if (!strongOperation || strongOperation.isCancelled) {
|
186
|
+ // `SDWebImageCombinedOperation` -> `SDWebImageDownloadToken` -> `downloadOperationCancelToken`, which is a `SDCallbacksDictionary` and retain the completed block bellow, so we need weak-strong again to avoid retain cycle
|
|
|
187
|
+ __weak typeof(strongOperation) weakSubOperation = strongOperation;
|
|
|
188
|
+ strongOperation.downloadToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
|
|
|
189
|
+ __strong typeof(weakSubOperation) strongSubOperation = weakSubOperation;
|
|
|
190
|
+ if (!strongSubOperation || strongSubOperation.isCancelled) {
|
186
|
// Do nothing if the operation was cancelled
|
191
|
// Do nothing if the operation was cancelled
|
187
|
// See #699 for more details
|
192
|
// See #699 for more details
|
188
|
// if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
|
193
|
// if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
|
189
|
} else if (error) {
|
194
|
} else if (error) {
|
190
|
- [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];
|
195
|
+ [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
|
191
|
|
196
|
|
192
|
if ( error.code != NSURLErrorNotConnectedToInternet
|
197
|
if ( error.code != NSURLErrorNotConnectedToInternet
|
193
|
&& error.code != NSURLErrorCancelled
|
198
|
&& error.code != NSURLErrorCancelled
|
|
@@ -228,37 +233,27 @@ |
|
@@ -228,37 +233,27 @@ |
228
|
[self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
|
233
|
[self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
|
229
|
}
|
234
|
}
|
230
|
|
235
|
|
231
|
- [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
236
|
+ [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
232
|
});
|
237
|
});
|
233
|
} else {
|
238
|
} else {
|
234
|
if (downloadedImage && finished) {
|
239
|
if (downloadedImage && finished) {
|
235
|
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
|
240
|
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
|
236
|
}
|
241
|
}
|
237
|
- [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
242
|
+ [self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
238
|
}
|
243
|
}
|
239
|
}
|
244
|
}
|
240
|
|
245
|
|
241
|
if (finished) {
|
246
|
if (finished) {
|
242
|
- [self safelyRemoveOperationFromRunning:strongOperation];
|
247
|
+ [self safelyRemoveOperationFromRunning:strongSubOperation];
|
243
|
}
|
248
|
}
|
244
|
}];
|
249
|
}];
|
245
|
- @synchronized(operation) {
|
|
|
246
|
- // Need same lock to ensure cancelBlock called because cancel method can be called in different queue
|
|
|
247
|
- operation.cancelBlock = ^{
|
|
|
248
|
- [self.imageDownloader cancel:subOperationToken];
|
|
|
249
|
- __strong __typeof(weakOperation) strongOperation = weakOperation;
|
|
|
250
|
- [self safelyRemoveOperationFromRunning:strongOperation];
|
|
|
251
|
- };
|
|
|
252
|
- }
|
|
|
253
|
} else if (cachedImage) {
|
250
|
} else if (cachedImage) {
|
254
|
- __strong __typeof(weakOperation) strongOperation = weakOperation;
|
|
|
255
|
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
251
|
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
256
|
- [self safelyRemoveOperationFromRunning:operation];
|
252
|
+ [self safelyRemoveOperationFromRunning:strongOperation];
|
257
|
} else {
|
253
|
} else {
|
258
|
// Image not in cache and download disallowed by delegate
|
254
|
// Image not in cache and download disallowed by delegate
|
259
|
- __strong __typeof(weakOperation) strongOperation = weakOperation;
|
|
|
260
|
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
|
255
|
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
|
261
|
- [self safelyRemoveOperationFromRunning:operation];
|
256
|
+ [self safelyRemoveOperationFromRunning:strongOperation];
|
262
|
}
|
257
|
}
|
263
|
}];
|
258
|
}];
|
264
|
|
259
|
|
|
@@ -323,18 +318,6 @@ |
|
@@ -323,18 +318,6 @@ |
323
|
|
318
|
|
324
|
@implementation SDWebImageCombinedOperation
|
319
|
@implementation SDWebImageCombinedOperation
|
325
|
|
320
|
|
326
|
-- (void)setCancelBlock:(nullable SDWebImageNoParamsBlock)cancelBlock {
|
|
|
327
|
- // check if the operation is already cancelled, then we just call the cancelBlock
|
|
|
328
|
- if (self.isCancelled) {
|
|
|
329
|
- if (cancelBlock) {
|
|
|
330
|
- cancelBlock();
|
|
|
331
|
- }
|
|
|
332
|
- _cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes
|
|
|
333
|
- } else {
|
|
|
334
|
- _cancelBlock = [cancelBlock copy];
|
|
|
335
|
- }
|
|
|
336
|
-}
|
|
|
337
|
-
|
|
|
338
|
- (void)cancel {
|
321
|
- (void)cancel {
|
339
|
@synchronized(self) {
|
322
|
@synchronized(self) {
|
340
|
self.cancelled = YES;
|
323
|
self.cancelled = YES;
|
|
@@ -342,10 +325,10 @@ |
|
@@ -342,10 +325,10 @@ |
342
|
[self.cacheOperation cancel];
|
325
|
[self.cacheOperation cancel];
|
343
|
self.cacheOperation = nil;
|
326
|
self.cacheOperation = nil;
|
344
|
}
|
327
|
}
|
345
|
- if (self.cancelBlock) {
|
|
|
346
|
- self.cancelBlock();
|
|
|
347
|
- self.cancelBlock = nil;
|
328
|
+ if (self.downloadToken) {
|
|
|
329
|
+ [self.manager.imageDownloader cancel:self.downloadToken];
|
348
|
}
|
330
|
}
|
|
|
331
|
+ [self.manager safelyRemoveOperationFromRunning:self];
|
349
|
}
|
332
|
}
|
350
|
}
|
333
|
}
|
351
|
|
334
|
|