Ensure image isn't decoded twice if not necessary when SDWebImageRefreshCached flag is used #326
Showing
5 changed files
with
55 additions
and
21 deletions
@@ -14,7 +14,16 @@ typedef enum | @@ -14,7 +14,16 @@ typedef enum | ||
14 | { | 14 | { |
15 | SDWebImageDownloaderLowPriority = 1 << 0, | 15 | SDWebImageDownloaderLowPriority = 1 << 0, |
16 | SDWebImageDownloaderProgressiveDownload = 1 << 1, | 16 | SDWebImageDownloaderProgressiveDownload = 1 << 1, |
17 | - SDWebImageDownloaderEnableNSURLCache = 1 << 2 | 17 | + /** |
18 | + * By default, request prevent the of NSURLCache. With this flag, NSURLCache | ||
19 | + * is used with default policies. | ||
20 | + */ | ||
21 | + SDWebImageDownloaderUseNSURLCache = 1 << 2, | ||
22 | + /** | ||
23 | + * Call completion block with nil image/imageData if the image was read from NSURLCache | ||
24 | + * (to be combined with `SDWebImageDownloaderUseNSURLCache`). | ||
25 | + */ | ||
26 | + SDWebImageDownloaderIgnoreCachedResponse = 1 << 3 | ||
18 | } SDWebImageDownloaderOptions; | 27 | } SDWebImageDownloaderOptions; |
19 | 28 | ||
20 | typedef enum | 29 | typedef enum |
@@ -120,7 +120,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; | @@ -120,7 +120,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; | ||
120 | [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^ | 120 | [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^ |
121 | { | 121 | { |
122 | // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise | 122 | // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise |
123 | - NSMutableURLRequest *request = [NSMutableURLRequest.alloc initWithURL:url cachePolicy:(options & SDWebImageDownloaderEnableNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:15]; | 123 | + NSMutableURLRequest *request = [NSMutableURLRequest.alloc initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:15]; |
124 | request.HTTPShouldHandleCookies = NO; | 124 | request.HTTPShouldHandleCookies = NO; |
125 | request.HTTPShouldUsePipelining = YES; | 125 | request.HTTPShouldUsePipelining = YES; |
126 | request.allHTTPHeaderFields = wself.HTTPHeaders; | 126 | request.allHTTPHeaderFields = wself.HTTPHeaders; |
@@ -28,6 +28,7 @@ | @@ -28,6 +28,7 @@ | ||
28 | @implementation SDWebImageDownloaderOperation | 28 | @implementation SDWebImageDownloaderOperation |
29 | { | 29 | { |
30 | size_t width, height; | 30 | size_t width, height; |
31 | + BOOL responseFromCached; | ||
31 | } | 32 | } |
32 | 33 | ||
33 | - (id)initWithRequest:(NSURLRequest *)request queue:(dispatch_queue_t)queue options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSData *, NSError *, BOOL))completedBlock cancelled:(void (^)())cancelBlock | 34 | - (id)initWithRequest:(NSURLRequest *)request queue:(dispatch_queue_t)queue options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSData *, NSError *, BOOL))completedBlock cancelled:(void (^)())cancelBlock |
@@ -43,6 +44,7 @@ | @@ -43,6 +44,7 @@ | ||
43 | _executing = NO; | 44 | _executing = NO; |
44 | _finished = NO; | 45 | _finished = NO; |
45 | _expectedSize = 0; | 46 | _expectedSize = 0; |
47 | + responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called | ||
46 | } | 48 | } |
47 | return self; | 49 | return self; |
48 | } | 50 | } |
@@ -271,25 +273,35 @@ | @@ -271,25 +273,35 @@ | ||
271 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; | 273 | [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; |
272 | 274 | ||
273 | SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock; | 275 | SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock; |
276 | + | ||
274 | if (completionBlock) | 277 | if (completionBlock) |
275 | { | 278 | { |
276 | - dispatch_async(self.queue, ^ | 279 | + if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) |
280 | + { | ||
281 | + completionBlock(nil, nil, nil, YES); | ||
282 | + self.completionBlock = nil; | ||
283 | + [self done]; | ||
284 | + } | ||
285 | + else | ||
277 | { | 286 | { |
278 | - UIImage *image = [UIImage decodedImageWithImage:SDScaledImageForPath(self.request.URL.absoluteString, self.imageData)]; | ||
279 | - dispatch_async(dispatch_get_main_queue(), ^ | 287 | + dispatch_async(self.queue, ^ |
280 | { | 288 | { |
281 | - if (CGSizeEqualToSize(image.size, CGSizeZero)) | ||
282 | - { | ||
283 | - completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Downloaded image has 0 pixels"}], YES); | ||
284 | - } | ||
285 | - else | 289 | + UIImage *image = [UIImage decodedImageWithImage:SDScaledImageForPath(self.request.URL.absoluteString, self.imageData)]; |
290 | + dispatch_async(dispatch_get_main_queue(), ^ | ||
286 | { | 291 | { |
287 | - completionBlock(image, self.imageData, nil, YES); | ||
288 | - } | ||
289 | - self.completionBlock = nil; | ||
290 | - [self done]; | 292 | + if (CGSizeEqualToSize(image.size, CGSizeZero)) |
293 | + { | ||
294 | + completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Downloaded image has 0 pixels"}], YES); | ||
295 | + } | ||
296 | + else | ||
297 | + { | ||
298 | + completionBlock(image, self.imageData, nil, YES); | ||
299 | + } | ||
300 | + self.completionBlock = nil; | ||
301 | + [self done]; | ||
302 | + }); | ||
291 | }); | 303 | }); |
292 | - }); | 304 | + } |
293 | } | 305 | } |
294 | else | 306 | else |
295 | { | 307 | { |
@@ -311,6 +323,7 @@ | @@ -311,6 +323,7 @@ | ||
311 | 323 | ||
312 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse | 324 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse |
313 | { | 325 | { |
326 | + responseFromCached = NO; // If this method is called, it means the response wasn't read from cache | ||
314 | if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) | 327 | if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) |
315 | { | 328 | { |
316 | // Prevents caching of responses | 329 | // Prevents caching of responses |
@@ -33,10 +33,12 @@ typedef enum | @@ -33,10 +33,12 @@ typedef enum | ||
33 | */ | 33 | */ |
34 | SDWebImageProgressiveDownload = 1 << 3, | 34 | SDWebImageProgressiveDownload = 1 << 3, |
35 | /** | 35 | /** |
36 | - * Even if the image is cached, fetch the URL again anyway. When set, NSURLCache is enabled in the downloader. | ||
37 | - * NSURLCache will handle the protocol caching logic while SDWebImage remains useful for offline images. | 36 | + * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed. |
37 | + * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation. | ||
38 | * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics. | 38 | * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics. |
39 | - * If a cached image exists, the completion block is called once with the cached image and again with the final image. | 39 | + * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image. |
40 | + * | ||
41 | + * Use this flag only if you can't make your URLs static with embeded cache busting parameter. | ||
40 | */ | 42 | */ |
41 | SDWebImageRefreshCached = 1 << 4 | 43 | SDWebImageRefreshCached = 1 << 4 |
42 | } SDWebImageOptions; | 44 | } SDWebImageOptions; |
@@ -107,8 +107,14 @@ | @@ -107,8 +107,14 @@ | ||
107 | SDWebImageDownloaderOptions downloaderOptions = 0; | 107 | SDWebImageDownloaderOptions downloaderOptions = 0; |
108 | if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; | 108 | if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; |
109 | if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; | 109 | if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; |
110 | - if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderEnableNSURLCache; | ||
111 | - if (image && options & SDWebImageRefreshCached) downloaderOptions ^= SDWebImageDownloaderProgressiveDownload; // force progressive off if image is refreshing | 110 | + if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; |
111 | + if (image && options & SDWebImageRefreshCached) | ||
112 | + { | ||
113 | + // force progressive off if image already cached but forced refreshing | ||
114 | + downloaderOptions ^= SDWebImageDownloaderProgressiveDownload; | ||
115 | + // ignore image read from NSURLCache if image if cached but force refreshing | ||
116 | + downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; | ||
117 | + } | ||
112 | __block id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) | 118 | __block id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) |
113 | { | 119 | { |
114 | if (weakOperation.cancelled) | 120 | if (weakOperation.cancelled) |
@@ -138,7 +144,11 @@ | @@ -138,7 +144,11 @@ | ||
138 | cacheOnDisk = NO; | 144 | cacheOnDisk = NO; |
139 | } | 145 | } |
140 | 146 | ||
141 | - if (downloadedImage && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) | 147 | + if (options & SDWebImageRefreshCached && image && !downloadedImage) |
148 | + { | ||
149 | + // Image refresh hit the NSURLCache cache, do not call the completion block | ||
150 | + } | ||
151 | + else if (downloadedImage && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) | ||
142 | { | 152 | { |
143 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ | 153 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ |
144 | { | 154 | { |
-
Please register or login to post a comment