Merge pull request #2966 from dreampiggy/bugfix_refactory_thumbnail_cache_key

Refactory the current thumbnail && transformer about cache key. Developer should have the API to calcualte the cache key from thumbnail or transformer, not hard-coded.
This commit is contained in:
DreamPiggy 2020-04-03 16:30:39 +08:00 committed by GitHub
commit 897e38c1b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 109 additions and 32 deletions

View File

@ -227,8 +227,7 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
#pragma mark - Query and Retrieve Ops
/**
* Asynchronously queries the cache with operation and call the completion when done.
* Query the image data for the given key synchronously.
* Synchronously query the image data for the given key in disk cache. You can decode the image data to image after loaded.
*
* @param key The unique key used to store the wanted image
* @return The image data for the given key, or nil if not found.
@ -236,18 +235,18 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
- (nullable NSData *)diskImageDataForKey:(nullable NSString *)key;
/**
* Asynchronously load the image data in disk cache. You can decode the image data to image after loaded.
* Asynchronously query the image data for the given key in disk cache. You can decode the image data to image after loaded.
*
* @param key The unique key used to store the wanted image
* @param completionBlock the block to be executed when the check is done.
* @param completionBlock the block to be executed when the query is done.
* @note the completion block will be always executed on the main queue
*/
- (void)diskImageDataQueryForKey:(nullable NSString *)key completion:(nullable SDImageCacheQueryDataCompletionBlock)completionBlock;
/**
* Operation that queries the cache asynchronously and call the completion when done.
* Asynchronously queries the cache with operation and call the completion when done.
*
* @param key The unique key used to store the wanted image
* @param key The unique key used to store the wanted image. If you want transformed or thumbnail image, calculate the key with `SDTransformedKeyForKey`, `SDThumbnailedKeyForKey`, or generate the cache key from url with `cacheKeyForURL:context:`.
* @param doneBlock The completion block. Will not get called if the operation is cancelled
*
* @return a NSOperation instance containing the cache op
@ -257,7 +256,7 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
/**
* Asynchronously queries the cache with operation and call the completion when done.
*
* @param key The unique key used to store the wanted image
* @param key The unique key used to store the wanted image. If you want transformed or thumbnail image, calculate the key with `SDTransformedKeyForKey`, `SDThumbnailedKeyForKey`, or generate the cache key from url with `cacheKeyForURL:context:`.
* @param options A mask to specify options to use for this cache query
* @param doneBlock The completion block. Will not get called if the operation is cancelled
*
@ -268,7 +267,7 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
/**
* Asynchronously queries the cache with operation and call the completion when done.
*
* @param key The unique key used to store the wanted image
* @param key The unique key used to store the wanted image. If you want transformed or thumbnail image, calculate the key with `SDTransformedKeyForKey`, `SDThumbnailedKeyForKey`, or generate the cache key from url with `cacheKeyForURL:context:`.
* @param options A mask to specify options to use for this cache query
* @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
* @param doneBlock The completion block. Will not get called if the operation is cancelled
@ -306,6 +305,16 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
*/
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
/**
* Synchronously query the disk cache. With the options and context which may effect the image generation. (Such as transformer, animated image, thumbnail, etc)
*
* @param key The unique key used to store the image
* @param options A mask to specify options to use for this cache query
* @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
* @return The image for the given key, or nil if not found.
*/
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context;
/**
* Synchronously query the cache (memory and or disk) after checking the memory cache.
*
@ -314,6 +323,16 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
*/
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
/**
* Synchronously query the cache (memory and or disk) after checking the memory cache. With the options and context which may effect the image generation. (Such as transformer, animated image, thumbnail, etc)
*
* @param key The unique key used to store the image
* @param options A mask to specify options to use for this cache query
* @param context A context contains different options to perform specify changes or processes, see `SDWebImageContextOption`. This hold the extra objects which `options` enum can not hold.
* @return The image for the given key, or nil if not found.
*/
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context;;
#pragma mark - Remove Ops
/**

View File

@ -9,7 +9,6 @@
#import "SDImageCache.h"
#import "NSImage+Compatibility.h"
#import "SDImageCodersManager.h"
#import "SDImageTransformer.h"
#import "SDImageCoderHelper.h"
#import "SDAnimatedImage.h"
#import "UIImage+MemoryCacheCost.h"
@ -342,7 +341,12 @@
}
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
UIImage *diskImage = [self diskImageForKey:key];
return [self imageFromDiskCacheForKey:key options:0 context:nil];
}
- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context {
NSData *data = [self diskImageDataForKey:key];
UIImage *diskImage = [self diskImageForKey:key data:data options:options context:context];
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = diskImage.sd_memoryCost;
[self.memoryCache setObject:diskImage forKey:key cost:cost];
@ -352,6 +356,10 @@
}
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key {
return [self imageFromCacheForKey:key options:0 context:nil];
}
- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context {
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
@ -359,7 +367,7 @@
}
// Second check the disk cache...
image = [self imageFromDiskCacheForKey:key];
image = [self imageFromDiskCacheForKey:key options:options context:context];
return image;
}
@ -455,13 +463,6 @@
return nil;
}
id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
if (transformer) {
// grab the transformed disk image if transformer provided
NSString *transformerKey = [transformer transformerKey];
key = SDTransformedKeyForKey(key, transformerKey);
}
// First check the in-memory cache...
UIImage *image;
if (queryCacheType != SDImageCacheTypeDisk) {

View File

@ -61,7 +61,7 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeCompressio
But this may be useful for some custom coders, because some business logic may dependent on things other than image or image data inforamtion only.
See `SDWebImageContext` for more detailed information.
*/
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderWebImageContext;
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderWebImageContext API_DEPRECATED("The coder component will be seperated from Core subspec in the future. Update your code to not rely on this context option.", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));;
#pragma mark - Coder
/**

View File

@ -18,6 +18,16 @@
*/
FOUNDATION_EXPORT NSString * _Nullable SDTransformedKeyForKey(NSString * _Nullable key, NSString * _Nonnull transformerKey);
/**
Return the thumbnailed cache key which applied with specify thumbnailSize and preserveAspectRatio control.
@param key The original cache key
@param thumbnailPixelSize The thumbnail pixel size
@param preserveAspectRatio The preserve aspect ratio option
@return The thumbnailed cache key
@note If you have both transformer and thumbnail applied for image, call `SDThumbnailedKeyForKey` firstly and then with `SDTransformedKeyForKey`.`
*/
FOUNDATION_EXPORT NSString * _Nullable SDThumbnailedKeyForKey(NSString * _Nullable key, CGSize thumbnailPixelSize, BOOL preserveAspectRatio);
/**
A transformer protocol to transform the image load from cache or from download.
You can provide transformer to cache and manager (Through the `transformer` property or context option `SDWebImageContextImageTransformer`).
@ -38,10 +48,10 @@ FOUNDATION_EXPORT NSString * _Nullable SDTransformedKeyForKey(NSString * _Nullab
Transform the image to another image.
@param image The image to be transformed
@param key The cache key associated to the image
@param key The cache key associated to the image. This arg is a hint for image source, not always useful and should be nullable. In the future we will remove this arg.
@return The transformed image, or nil if transform failed
*/
- (nullable UIImage *)transformedImageWithImage:(nonnull UIImage *)image forKey:(nonnull NSString *)key;
- (nullable UIImage *)transformedImageWithImage:(nonnull UIImage *)image forKey:(nonnull NSString *)key API_DEPRECATED("The key arg will be removed in the future. Update your code and don't rely on that.", macos(10.10, API_TO_BE_DEPRECATED), ios(8.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED));
@end

View File

@ -38,6 +38,11 @@ NSString * _Nullable SDTransformedKeyForKey(NSString * _Nullable key, NSString *
}
}
NSString * _Nullable SDThumbnailedKeyForKey(NSString * _Nullable key, CGSize thumbnailPixelSize, BOOL preserveAspectRatio) {
NSString *thumbnailKey = [NSString stringWithFormat:@"Thumbnail({%f,%f},%d)", thumbnailPixelSize.width, thumbnailPixelSize.height, preserveAspectRatio];
return SDTransformedKeyForKey(key, thumbnailKey);
}
@interface SDImagePipelineTransformer ()
@property (nonatomic, copy, readwrite, nonnull) NSArray<id<SDImageTransformer>> *transformers;

View File

@ -237,7 +237,7 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageL
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageCoder;
/**
A id<SDImageTransformer> instance which conforms `SDImageTransformer` protocol. It's used for image transform after the image load finished and store the transformed image to cache. If you provide one, it will ignore the `transformer` in manager and use provided one instead. (id<SDImageTransformer>)
A id<SDImageTransformer> instance which conforms `SDImageTransformer` protocol. It's used for image transform after the image load finished and store the transformed image to cache. If you provide one, it will ignore the `transformer` in manager and use provided one instead. If you pass NSNull, the transformer feature will be disabled. (id<SDImageTransformer>)
*/
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageTransformer;

View File

@ -262,8 +262,15 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager];
- (void)cancelAll;
/**
* Return the cache key for a given URL
* Return the cache key for a given URL, does not considerate transformer or thumbnail.
* @note This method does not have context option, only use the url and manager level cacheKeyFilter to generate the cache key.
*/
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;
/**
* Return the cache key for a given URL and context option.
* @note The context option like `.thumbnailPixelSize` and `.imageTransformer` will effect the generated cache key, using this if you have those context associated.
*/
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url context:(nullable SDWebImageContext *)context;
@end

View File

@ -95,7 +95,20 @@ static id<SDImageLoader> _defaultImageLoader;
}
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url {
return [self cacheKeyForURL:url context:nil];
if (!url) {
return @"";
}
NSString *key;
// Cache Key Filter
id<SDWebImageCacheKeyFilter> cacheKeyFilter = self.cacheKeyFilter;
if (cacheKeyFilter) {
key = [cacheKeyFilter cacheKeyForURL:url];
} else {
key = url.absoluteString;
}
return key;
}
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url context:(nullable SDWebImageContext *)context {
@ -114,6 +127,7 @@ static id<SDImageLoader> _defaultImageLoader;
} else {
key = url.absoluteString;
}
// Thumbnail Key Appending
NSValue *thumbnailSizeValue = context[SDWebImageContextImageThumbnailPixelSize];
if (thumbnailSizeValue != nil) {
@ -123,14 +137,24 @@ static id<SDImageLoader> _defaultImageLoader;
#else
thumbnailSize = thumbnailSizeValue.CGSizeValue;
#endif
BOOL preserveAspectRatio = YES;
NSNumber *preserveAspectRatioValue = context[SDWebImageContextImagePreserveAspectRatio];
if (preserveAspectRatioValue != nil) {
preserveAspectRatio = preserveAspectRatioValue.boolValue;
}
NSString *transformerKey = [NSString stringWithFormat:@"Thumbnail({%f,%f},%d)", thumbnailSize.width, thumbnailSize.height, preserveAspectRatio];
key = SDTransformedKeyForKey(key, transformerKey);
key = SDThumbnailedKeyForKey(key, thumbnailSize, preserveAspectRatio);
}
// Transformer Key Appending
id<SDImageTransformer> transformer = self.transformer;
if (context[SDWebImageContextImageTransformer]) {
transformer = context[SDWebImageContextImageTransformer];
if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) {
transformer = nil;
}
}
if (transformer) {
key = SDTransformedKeyForKey(key, transformer.transformerKey);
}
return key;
@ -347,8 +371,15 @@ static id<SDImageLoader> _defaultImageLoader;
if (context[SDWebImageContextOriginalStoreCacheType]) {
originalStoreCacheType = [context[SDWebImageContextOriginalStoreCacheType] integerValue];
}
NSString *key = [self cacheKeyForURL:url context:context];
// origin cache key
SDWebImageMutableContext *originContext = [context mutableCopy];
// disable transformer for cache key generation
originContext[SDWebImageContextImageTransformer] = [NSNull null];
NSString *key = [self cacheKeyForURL:url context:originContext];
id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) {
transformer = nil;
}
id<SDWebImageCacheSerializer> cacheSerializer = context[SDWebImageContextCacheSerializer];
BOOL shouldTransformImage = downloadedImage && transformer;
@ -397,9 +428,14 @@ static id<SDImageLoader> _defaultImageLoader;
if (context[SDWebImageContextStoreCacheType]) {
storeCacheType = [context[SDWebImageContextStoreCacheType] integerValue];
}
// transformed cache key
NSString *key = [self cacheKeyForURL:url context:context];
id<SDImageTransformer> transformer = context[SDWebImageContextImageTransformer];
if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) {
transformer = nil;
}
id<SDWebImageCacheSerializer> cacheSerializer = context[SDWebImageContextCacheSerializer];
BOOL shouldTransformImage = originalImage && transformer;
shouldTransformImage = shouldTransformImage && (!originalImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage));
shouldTransformImage = shouldTransformImage && (!originalImage.sd_isVector || (options & SDWebImageTransformVectorImage));
@ -409,8 +445,6 @@ static id<SDImageLoader> _defaultImageLoader;
@autoreleasepool {
UIImage *transformedImage = [transformer transformedImageWithImage:originalImage forKey:key];
if (transformedImage && finished) {
NSString *transformerKey = [transformer transformerKey];
NSString *cacheKey = SDTransformedKeyForKey(key, transformerKey);
BOOL imageWasTransformed = ![transformedImage isEqual:originalImage];
NSData *cacheData;
// pass nil if the image was transformed, so we can recalculate the data from the image
@ -421,7 +455,7 @@ static id<SDImageLoader> _defaultImageLoader;
}
// keep the original image format and extended data
SDImageCopyAssociatedObject(originalImage, transformedImage);
[self storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType options:options context:context completion:^{
[self storeImage:transformedImage imageData:cacheData forKey:key cacheType:storeCacheType options:options context:context completion:^{
[self callCompletionBlockForOperation:operation completion:completedBlock image:transformedImage data:originalData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
}];
} else {

View File

@ -220,6 +220,7 @@
// Use a fresh manager && cache to avoid get effected by other test cases
SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"SDWebImageStoreCacheType"];
[cache clearDiskOnCompletion:nil];
SDWebImageManager *manager = [[SDWebImageManager alloc] initWithCache:cache loader:SDWebImageDownloader.sharedDownloader];
SDWebImageTestTransformer *transformer = [[SDWebImageTestTransformer alloc] init];
transformer.testImage = [[UIImage alloc] initWithContentsOfFile:[self testJPEGPath]];
@ -229,7 +230,7 @@
SDWebImageContext *context = @{SDWebImageContextOriginalStoreCacheType : @(SDImageCacheTypeDisk), SDWebImageContextStoreCacheType : @(SDImageCacheTypeMemory)};
NSURL *url = [NSURL URLWithString:kTestJPEGURL];
NSString *originalKey = [manager cacheKeyForURL:url];
NSString *transformedKey = SDTransformedKeyForKey(originalKey, transformer.transformerKey);
NSString *transformedKey = [manager cacheKeyForURL:url context:context];
[manager loadImageWithURL:url options:SDWebImageTransformAnimatedImage context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
expect(image).equal(transformer.testImage);