Authored by DreamPiggy
Committed by GitHub

Merge pull request #2162 from dreampiggy/feature_query_cache_options

Feature query cache options
@@ -46,8 +46,10 @@ @@ -46,8 +46,10 @@
46 options:(SDWebImageOptions)options 46 options:(SDWebImageOptions)options
47 progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 47 progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
48 completed:(nullable SDExternalCompletionBlock)completedBlock { 48 completed:(nullable SDExternalCompletionBlock)completedBlock {
49 - __weak typeof(self)weakSelf = self; 49 + options |= SDWebImageQueryDiskDataWhenInMemory;
  50 + options |= SDWebImageQueryDiskDataSync;
50 dispatch_group_t group = dispatch_group_create(); 51 dispatch_group_t group = dispatch_group_create();
  52 + __weak typeof(self)weakSelf = self;
51 [self sd_internalSetImageWithURL:url 53 [self sd_internalSetImageWithURL:url
52 placeholderImage:placeholder 54 placeholderImage:placeholder
53 options:options 55 options:options
@@ -25,6 +25,17 @@ typedef NS_ENUM(NSInteger, SDImageCacheType) { @@ -25,6 +25,17 @@ typedef NS_ENUM(NSInteger, SDImageCacheType) {
25 SDImageCacheTypeMemory 25 SDImageCacheTypeMemory
26 }; 26 };
27 27
  28 +typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
  29 + /**
  30 + * By default, we do not query disk cache when the image is cached in memory. This mask can force query disk data at the same time.
  31 + */
  32 + SDImageCacheQueryDiskDataWhenInMemory = 1 << 0,
  33 + /**
  34 + * By default, we query the memory cache synchonized, disk cache asynchronized. This mask can force to query disk cache synchonized.
  35 + */
  36 + SDImageCacheQueryDiskDataSync = 1 << 1
  37 +};
  38 +
28 typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType); 39 typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType);
29 40
30 typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); 41 typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
@@ -167,6 +178,17 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot @@ -167,6 +178,17 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
167 - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock; 178 - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
168 179
169 /** 180 /**
  181 + * Operation that queries the cache asynchronously and call the completion when done.
  182 + *
  183 + * @param key The unique key used to store the wanted image
  184 + * @param options A mask to specify options to use for this cache query
  185 + * @param doneBlock The completion block. Will not get called if the operation is cancelled
  186 + *
  187 + * @return a NSOperation instance containing the cache op
  188 + */
  189 +- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;
  190 +
  191 +/**
170 * Query the memory cache synchronously. 192 * Query the memory cache synchronously.
171 * 193 *
172 * @param key The unique key used to store the image 194 * @param key The unique key used to store the image
@@ -322,6 +322,10 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -322,6 +322,10 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
322 322
323 - (nullable UIImage *)diskImageForKey:(nullable NSString *)key { 323 - (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
324 NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; 324 NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
  325 + return [self diskImageForKey:key data:data];
  326 +}
  327 +
  328 +- (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data {
325 if (data) { 329 if (data) {
326 UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:data]; 330 UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:data];
327 image = [self scaledImageForKey:key image:image]; 331 image = [self scaledImageForKey:key image:image];
@@ -338,50 +342,64 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -338,50 +342,64 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
338 return SDScaledImageForKey(key, image); 342 return SDScaledImageForKey(key, image);
339 } 343 }
340 344
341 -- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock { 345 +- (NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDCacheQueryCompletedBlock)doneBlock {
  346 + return [self queryCacheOperationForKey:key options:0 done:doneBlock];
  347 +}
  348 +
  349 +- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock {
342 if (!key) { 350 if (!key) {
343 if (doneBlock) { 351 if (doneBlock) {
344 doneBlock(nil, nil, SDImageCacheTypeNone); 352 doneBlock(nil, nil, SDImageCacheTypeNone);
345 } 353 }
346 return nil; 354 return nil;
347 } 355 }
348 - 356 +
349 // First check the in-memory cache... 357 // First check the in-memory cache...
350 UIImage *image = [self imageFromMemoryCacheForKey:key]; 358 UIImage *image = [self imageFromMemoryCacheForKey:key];
351 - if (image) {  
352 - NSData *diskData = nil;  
353 - if (image.images) {  
354 - diskData = [self diskImageDataBySearchingAllPathsForKey:key];  
355 - } 359 + if (image && !(options & SDImageCacheQueryDiskDataWhenInMemory)) {
356 if (doneBlock) { 360 if (doneBlock) {
357 - doneBlock(image, diskData, SDImageCacheTypeMemory); 361 + doneBlock(image, nil, SDImageCacheTypeMemory);
358 } 362 }
359 return nil; 363 return nil;
360 } 364 }
361 - 365 +
362 NSOperation *operation = [NSOperation new]; 366 NSOperation *operation = [NSOperation new];
363 - dispatch_async(self.ioQueue, ^{ 367 + void(^queryDiskBlock)(void) = ^{
364 if (operation.isCancelled) { 368 if (operation.isCancelled) {
365 // do not call the completion if cancelled 369 // do not call the completion if cancelled
366 return; 370 return;
367 } 371 }
368 - 372 +
369 @autoreleasepool { 373 @autoreleasepool {
370 NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key]; 374 NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
371 - UIImage *diskImage = [self diskImageForKey:key];  
372 - if (diskImage && self.config.shouldCacheImagesInMemory) {  
373 - NSUInteger cost = SDCacheCostForImage(diskImage);  
374 - [self.memCache setObject:diskImage forKey:key cost:cost]; 375 + UIImage *diskImage = image;
  376 + if (!diskImage && diskData) {
  377 + // decode image data only if in-memory cache missed
  378 + diskImage = [self diskImageForKey:key data:diskData];
  379 + if (diskImage && self.config.shouldCacheImagesInMemory) {
  380 + NSUInteger cost = SDCacheCostForImage(diskImage);
  381 + [self.memCache setObject:diskImage forKey:key cost:cost];
  382 + }
375 } 383 }
376 - 384 +
377 if (doneBlock) { 385 if (doneBlock) {
378 - dispatch_async(dispatch_get_main_queue(), ^{ 386 + if (options & SDImageCacheQueryDiskDataSync) {
379 doneBlock(diskImage, diskData, SDImageCacheTypeDisk); 387 doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
380 - }); 388 + } else {
  389 + dispatch_async(dispatch_get_main_queue(), ^{
  390 + doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
  391 + });
  392 + }
381 } 393 }
382 } 394 }
383 - });  
384 - 395 + };
  396 +
  397 + if (options & SDImageCacheQueryDiskDataSync) {
  398 + queryDiskBlock();
  399 + } else {
  400 + dispatch_async(self.ioQueue, queryDiskBlock);
  401 + }
  402 +
385 return operation; 403 return operation;
386 } 404 }
387 405
@@ -105,13 +105,6 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over @@ -105,13 +105,6 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
105 return nil; 105 return nil;
106 } 106 }
107 107
108 - SDImageFormat format = [NSData sd_imageFormatForImageData:data];  
109 - if (format == SDImageFormatGIF) {  
110 - // static single GIF need to be created animated for `FLAnimatedImage` logic  
111 - // GIF does not support EXIF image orientation  
112 - image = [UIImage animatedImageWithImages:@[image] duration:image.duration];  
113 - return image;  
114 - }  
115 UIImageOrientation orientation = [[self class] sd_imageOrientationFromImageData:data]; 108 UIImageOrientation orientation = [[self class] sd_imageOrientationFromImageData:data];
116 if (orientation != UIImageOrientationUp) { 109 if (orientation != UIImageOrientationUp) {
117 image = [UIImage imageWithCGImage:image.CGImage 110 image = [UIImage imageWithCGImage:image.CGImage
@@ -94,7 +94,19 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) { @@ -94,7 +94,19 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
94 * images to a size compatible with the constrained memory of devices. 94 * images to a size compatible with the constrained memory of devices.
95 * If `SDWebImageProgressiveDownload` flag is set the scale down is deactivated. 95 * If `SDWebImageProgressiveDownload` flag is set the scale down is deactivated.
96 */ 96 */
97 - SDWebImageScaleDownLargeImages = 1 << 12 97 + SDWebImageScaleDownLargeImages = 1 << 12,
  98 +
  99 + /**
  100 + * By default, we do not query disk cache when the image is cached in memory. This mask can force query disk data at the same time.
  101 + * This options is recommend to be used with `SDWebImageQueryDiskDataSync` to ensure the image is loaded in the same runloop.
  102 + */
  103 + SDWebImageQueryDiskDataWhenInMemory = 1 << 13,
  104 +
  105 + /**
  106 + * By default, we query the memory cache synchonized, disk cache asynchronized. This mask can force to query disk cache synchonized to ensure that image is loaded in the same runloop.
  107 + * This can avoid flashing during cell reuse if you disable memory cache or in some other cases.
  108 + */
  109 + SDWebImageQueryDiskDataSync = 1 << 14
98 }; 110 };
99 111
100 typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL); 112 typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
@@ -143,8 +143,12 @@ @@ -143,8 +143,12 @@
143 [self.runningOperations addObject:operation]; 143 [self.runningOperations addObject:operation];
144 } 144 }
145 NSString *key = [self cacheKeyForURL:url]; 145 NSString *key = [self cacheKeyForURL:url];
  146 +
  147 + SDImageCacheOptions cacheOptions = 0;
  148 + if (options & SDWebImageQueryDiskDataWhenInMemory) cacheOptions |= SDImageCacheQueryDiskDataWhenInMemory;
  149 + if (options & SDWebImageQueryDiskDataSync) cacheOptions |= SDImageCacheQueryDiskDataSync;
146 150
147 - operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) { 151 + operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
148 if (operation.isCancelled) { 152 if (operation.isCancelled) {
149 [self safelyRemoveOperationFromRunning:operation]; 153 [self safelyRemoveOperationFromRunning:operation];
150 return; 154 return;