Opt the thumbnail cache behavior, now it prefers to store/query original data from disk, thumbnail image from memory

Separate the thumbnail from transformer, they are for different use case
This commit is contained in:
DreamPiggy 2022-06-23 02:50:30 +08:00
parent 3746c5d4c6
commit 613a2bf988
1 changed files with 110 additions and 44 deletions

View File

@ -112,6 +112,26 @@ static id<SDImageLoader> _defaultImageLoader;
return key; return key;
} }
- (nullable NSString *)originalCacheKeyForURL:(nullable NSURL *)url context:(nullable SDWebImageContext *)context {
if (!url) {
return @"";
}
NSString *key;
// Cache Key Filter
id<SDWebImageCacheKeyFilter> cacheKeyFilter = self.cacheKeyFilter;
if (context[SDWebImageContextCacheKeyFilter]) {
cacheKeyFilter = context[SDWebImageContextCacheKeyFilter];
}
if (cacheKeyFilter) {
key = [cacheKeyFilter cacheKeyForURL:url];
} else {
key = url.absoluteString;
}
return key;
}
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url context:(nullable SDWebImageContext *)context { - (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url context:(nullable SDWebImageContext *)context {
if (!url) { if (!url) {
return @""; return @"";
@ -278,10 +298,14 @@ static id<SDImageLoader> _defaultImageLoader;
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] url:url]; [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] url:url];
[self safelyRemoveOperationFromRunning:operation]; [self safelyRemoveOperationFromRunning:operation];
return; return;
} else if (context[SDWebImageContextImageTransformer] && !cachedImage) { } else if (!cachedImage) {
// Have a chance to query original cache instead of downloading BOOL mayInOriginalCache = context[SDWebImageContextImageTransformer] || context[SDWebImageContextImageThumbnailPixelSize];
[self callOriginalCacheProcessForOperation:operation url:url options:options context:context progress:progressBlock completed:completedBlock]; // Have a chance to query original cache instead of downloading, then applying transform
return; // Thumbnail decoding is done inside SDImageCache's decoding part, which does not need post processing for transform
if (mayInOriginalCache) {
[self callOriginalCacheProcessForOperation:operation url:url options:options context:context progress:progressBlock completed:completedBlock];
return;
}
} }
// Continue download process // Continue download process
@ -321,10 +345,8 @@ static id<SDImageLoader> _defaultImageLoader;
// Check whether we should query original cache // Check whether we should query original cache
BOOL shouldQueryOriginalCache = (originalQueryCacheType != SDImageCacheTypeNone); BOOL shouldQueryOriginalCache = (originalQueryCacheType != SDImageCacheTypeNone);
if (shouldQueryOriginalCache) { if (shouldQueryOriginalCache) {
// Disable transformer for original cache key generation // Get original cache key generation without transformer/thumbnail
SDWebImageMutableContext *tempContext = [context mutableCopy]; NSString *key = [self originalCacheKeyForURL:url context:context];
tempContext[SDWebImageContextImageTransformer] = [NSNull null];
NSString *key = [self cacheKeyForURL:url context:tempContext];
@weakify(operation); @weakify(operation);
operation.cacheOperation = [imageCache queryImageForKey:key options:options context:context cacheType:originalQueryCacheType completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) { operation.cacheOperation = [imageCache queryImageForKey:key options:options context:context cacheType:originalQueryCacheType completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
@strongify(operation); @strongify(operation);
@ -333,20 +355,20 @@ static id<SDImageLoader> _defaultImageLoader;
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] url:url]; [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"Operation cancelled by user during querying the cache"}] url:url];
[self safelyRemoveOperationFromRunning:operation]; [self safelyRemoveOperationFromRunning:operation];
return; return;
} else if (context[SDWebImageContextImageTransformer] && !cachedImage) { } else if (!cachedImage) {
// Original image cache miss. Continue download process // Original image cache miss. Continue download process
[self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:originalQueryCacheType progress:progressBlock completed:completedBlock]; [self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:SDImageCacheTypeNone progress:progressBlock completed:completedBlock];
return; return;
} }
// Use the store cache process instead of downloading, and ignore .refreshCached option for now // Use the store cache process instead of downloading, and ignore .refreshCached option for now
[self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:cachedImage downloadedData:cachedData finished:YES progress:progressBlock completed:completedBlock]; [self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:cachedImage downloadedData:cachedData cacheType:cacheType finished:YES completed:completedBlock];
[self safelyRemoveOperationFromRunning:operation]; [self safelyRemoveOperationFromRunning:operation];
}]; }];
} else { } else {
// Continue download process // Continue download process
[self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:originalQueryCacheType progress:progressBlock completed:completedBlock]; [self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:SDImageCacheTypeNone progress:progressBlock completed:completedBlock];
} }
} }
@ -420,7 +442,7 @@ static id<SDImageLoader> _defaultImageLoader;
SD_UNLOCK(self->_failedURLsLock); SD_UNLOCK(self->_failedURLsLock);
} }
// Continue store cache process // Continue store cache process
[self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:downloadedImage downloadedData:downloadedData finished:finished progress:progressBlock completed:completedBlock]; [self callStoreCacheProcessForOperation:operation url:url options:options context:context downloadedImage:downloadedImage downloadedData:downloadedData cacheType:SDImageCacheTypeNone finished:finished completed:completedBlock];
} }
if (finished) { if (finished) {
@ -444,8 +466,8 @@ static id<SDImageLoader> _defaultImageLoader;
context:(SDWebImageContext *)context context:(SDWebImageContext *)context
downloadedImage:(nullable UIImage *)downloadedImage downloadedImage:(nullable UIImage *)downloadedImage
downloadedData:(nullable NSData *)downloadedData downloadedData:(nullable NSData *)downloadedData
cacheType:(SDImageCacheType)cacheType
finished:(BOOL)finished finished:(BOOL)finished
progress:(nullable SDImageLoaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock { completed:(nullable SDInternalCompletionBlock)completedBlock {
// Grab the image cache to use, choose standalone original cache firstly // Grab the image cache to use, choose standalone original cache firstly
id<SDImageCache> imageCache; id<SDImageCache> imageCache;
@ -459,6 +481,7 @@ static id<SDImageLoader> _defaultImageLoader;
imageCache = self.imageCache; imageCache = self.imageCache;
} }
} }
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
// the target image store cache type // the target image store cache type
SDImageCacheType storeCacheType = SDImageCacheTypeAll; SDImageCacheType storeCacheType = SDImageCacheTypeAll;
if (context[SDWebImageContextStoreCacheType]) { if (context[SDWebImageContextStoreCacheType]) {
@ -469,10 +492,6 @@ static id<SDImageLoader> _defaultImageLoader;
if (context[SDWebImageContextOriginalStoreCacheType]) { if (context[SDWebImageContextOriginalStoreCacheType]) {
originalStoreCacheType = [context[SDWebImageContextOriginalStoreCacheType] integerValue]; originalStoreCacheType = [context[SDWebImageContextOriginalStoreCacheType] integerValue];
} }
// Disable transformer for original cache key generation
SDWebImageMutableContext *tempContext = [context mutableCopy];
tempContext[SDWebImageContextImageTransformer] = [NSNull null];
NSString *key = [self cacheKeyForURL:url context:tempContext];
id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer]; id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) { if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) {
transformer = nil; transformer = nil;
@ -482,31 +501,41 @@ static id<SDImageLoader> _defaultImageLoader;
BOOL shouldTransformImage = downloadedImage && transformer; BOOL shouldTransformImage = downloadedImage && transformer;
shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage)); shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage));
shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isVector || (options & SDWebImageTransformVectorImage)); shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isVector || (options & SDWebImageTransformVectorImage));
BOOL shouldCacheOriginal = downloadedImage && finished; BOOL shouldCacheOriginal = downloadedImage && finished && cacheType == SDImageCacheTypeNone;
// if available, store original image to cache // if available, store original image to cache
if (shouldCacheOriginal) { if (shouldCacheOriginal) {
// Get original cache key generation without transformer/thumbnail
NSString *key = [self originalCacheKeyForURL:url context:context];
// normally use the store cache type, but if target image is transformed, use original store cache type instead // normally use the store cache type, but if target image is transformed, use original store cache type instead
SDImageCacheType targetStoreCacheType = shouldTransformImage ? originalStoreCacheType : storeCacheType; SDImageCacheType targetStoreCacheType = shouldTransformImage ? originalStoreCacheType : storeCacheType;
if (cacheSerializer && (targetStoreCacheType == SDImageCacheTypeDisk || targetStoreCacheType == SDImageCacheTypeAll)) { UIImage *originalImage = downloadedImage;
BOOL thumbnailed = context[SDWebImageContextImageThumbnailPixelSize];
if (thumbnailed) {
// Thumbnail decoding does not keep original image
// Here we only store the original data to disk for original cache key
// Store thumbnail image to memory for thumbnail cache key later in `storeTransformCacheProcess`
originalImage = nil;
}
if (originalImage && cacheSerializer && (targetStoreCacheType == SDImageCacheTypeDisk || targetStoreCacheType == SDImageCacheTypeAll)) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@autoreleasepool { @autoreleasepool {
NSData *cacheData = [cacheSerializer cacheDataWithImage:downloadedImage originalData:downloadedData imageURL:url]; NSData *cacheData = [cacheSerializer cacheDataWithImage:originalImage originalData:downloadedData imageURL:url];
[self storeImage:downloadedImage imageData:cacheData forKey:key imageCache:imageCache cacheType:targetStoreCacheType options:options context:context completion:^{ [self storeImage:originalImage imageData:cacheData forKey:key imageCache:imageCache cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{
// Continue transform process // Continue transform process
[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData finished:finished progress:progressBlock completed:completedBlock]; [self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData cacheType:cacheType finished:finished completed:completedBlock];
}]; }];
} }
}); });
} else { } else {
[self storeImage:downloadedImage imageData:downloadedData forKey:key imageCache:imageCache cacheType:targetStoreCacheType options:options context:context completion:^{ [self storeImage:originalImage imageData:downloadedData forKey:key imageCache:imageCache cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{
// Continue transform process // Continue transform process
[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData finished:finished progress:progressBlock completed:completedBlock]; [self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData cacheType:cacheType finished:finished completed:completedBlock];
}]; }];
} }
} else { } else {
// Continue transform process // Continue transform process
[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData finished:finished progress:progressBlock completed:completedBlock]; [self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData cacheType:cacheType finished:finished completed:completedBlock];
} }
} }
@ -517,23 +546,14 @@ static id<SDImageLoader> _defaultImageLoader;
context:(SDWebImageContext *)context context:(SDWebImageContext *)context
originalImage:(nullable UIImage *)originalImage originalImage:(nullable UIImage *)originalImage
originalData:(nullable NSData *)originalData originalData:(nullable NSData *)originalData
cacheType:(SDImageCacheType)cacheType
finished:(BOOL)finished finished:(BOOL)finished
progress:(nullable SDImageLoaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock { completed:(nullable SDInternalCompletionBlock)completedBlock {
// Grab the image cache to use
id<SDImageCache> imageCache;
if ([context[SDWebImageContextImageCache] conformsToProtocol:@protocol(SDImageCache)]) {
imageCache = context[SDWebImageContextImageCache];
} else {
imageCache = self.imageCache;
}
// the target image store cache type // the target image store cache type
SDImageCacheType storeCacheType = SDImageCacheTypeAll; SDImageCacheType storeCacheType = SDImageCacheTypeAll;
if (context[SDWebImageContextStoreCacheType]) { if (context[SDWebImageContextStoreCacheType]) {
storeCacheType = [context[SDWebImageContextStoreCacheType] integerValue]; storeCacheType = [context[SDWebImageContextStoreCacheType] integerValue];
} }
// transformed cache key
NSString *key = [self cacheKeyForURL:url context:context];
id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer]; id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) { if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) {
transformer = nil; transformer = nil;
@ -547,6 +567,8 @@ static id<SDImageLoader> _defaultImageLoader;
if (shouldTransformImage) { if (shouldTransformImage) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@autoreleasepool { @autoreleasepool {
// transformed/thumbnailed cache key
NSString *key = [self cacheKeyForURL:url context:context];
UIImage *transformedImage = [transformer transformedImageWithImage:originalImage forKey:key]; UIImage *transformedImage = [transformer transformedImageWithImage:originalImage forKey:key];
if (transformedImage && finished) { if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:originalImage]; BOOL imageWasTransformed = ![transformedImage isEqual:originalImage];
@ -557,16 +579,62 @@ static id<SDImageLoader> _defaultImageLoader;
} else { } else {
cacheData = (imageWasTransformed ? nil : originalData); cacheData = (imageWasTransformed ? nil : originalData);
} }
[self storeImage:transformedImage imageData:cacheData forKey:key imageCache:imageCache cacheType:storeCacheType options:options context:context completion:^{ // Continue store transform cache process
[self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:originalData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; [self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:transformedImage data:cacheData cacheType:cacheType transformed:imageWasTransformed finished:finished completed:completedBlock];
}];
} else { } else {
[self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:originalData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; // Continue store transform cache process
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:originalImage data:originalData cacheType:cacheType transformed:NO finished:finished completed:completedBlock];
} }
} }
}); });
} else { } else {
[self callCompletionBlockForOperation:operation completion:completedBlock image:originalImage data:originalData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; // Continue store transform cache process
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:originalImage data:originalData cacheType:cacheType transformed:NO finished:finished completed:completedBlock];
}
}
- (void)callStoreTransformCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation
url:(nonnull NSURL *)url
options:(SDWebImageOptions)options
context:(SDWebImageContext *)context
image:(nullable UIImage *)image
data:(nullable NSData *)data
cacheType:(SDImageCacheType)cacheType
transformed:(BOOL)transformed
finished:(BOOL)finished
completed:(nullable SDInternalCompletionBlock)completedBlock {
// Grab the image cache to use
id<SDImageCache> imageCache;
if ([context[SDWebImageContextImageCache] conformsToProtocol:@protocol(SDImageCache)]) {
imageCache = context[SDWebImageContextImageCache];
} else {
imageCache = self.imageCache;
}
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
// the target image store cache type
SDImageCacheType storeCacheType = SDImageCacheTypeAll;
if (context[SDWebImageContextStoreCacheType]) {
storeCacheType = [context[SDWebImageContextStoreCacheType] integerValue];
}
// Hack: SDImageCache's queryImage API handle the thumbnail context option (in `SDImageCacheDecodeImageData`)
// but the storeImage does not handle the thumbnail context option
// to keep exist SDImageCache's impl compatible, we introduce this helper
NSData *cacheData = data;
BOOL thumbnailed = context[SDWebImageContextImageThumbnailPixelSize];
if (thumbnailed) {
// Thumbnail decoding already stored original data before in `storeCacheProcess`
// Here we only store the thumbnail image to memory for thumbnail cache key
cacheData = nil;
}
BOOL shouldCache = transformed || thumbnailed;
if (shouldCache) {
// transformed/thumbnailed cache key
NSString *key = [self cacheKeyForURL:url context:context];
[self storeImage:image imageData:cacheData forKey:key imageCache:imageCache cacheType:storeCacheType waitStoreCache:waitStoreCache completion:^{
[self callCompletionBlockForOperation:operation completion:completedBlock image:image data:data error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
}];
} else {
[self callCompletionBlockForOperation:operation completion:completedBlock image:image data:data error:nil cacheType:cacheType finished:finished url:url];
} }
} }
@ -586,10 +654,8 @@ static id<SDImageLoader> _defaultImageLoader;
forKey:(nullable NSString *)key forKey:(nullable NSString *)key
imageCache:(nonnull id<SDImageCache>)imageCache imageCache:(nonnull id<SDImageCache>)imageCache
cacheType:(SDImageCacheType)cacheType cacheType:(SDImageCacheType)cacheType
options:(SDWebImageOptions)options waitStoreCache:(BOOL)waitStoreCache
context:(nullable SDWebImageContext *)context
completion:(nullable SDWebImageNoParamsBlock)completion { completion:(nullable SDWebImageNoParamsBlock)completion {
BOOL waitStoreCache = SD_OPTIONS_CONTAINS(options, SDWebImageWaitStoreCache);
// Check whether we should wait the store cache finished. If not, callback immediately // Check whether we should wait the store cache finished. If not, callback immediately
[imageCache storeImage:image imageData:data forKey:key cacheType:cacheType completion:^{ [imageCache storeImage:image imageData:data forKey:key cacheType:cacheType completion:^{
if (waitStoreCache) { if (waitStoreCache) {