Merge pull request #3364 from dreampiggy/bugfix_thumbnail_full_loading_same_time_cache_issue

Fix the issue when thumbnail and full size loading at the same time, the thumbnail image will write to memory with wrong key
This commit is contained in:
DreamPiggy 2022-06-27 02:33:45 +08:00 committed by GitHub
commit 0805bc0f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 55 deletions

View File

@ -69,10 +69,8 @@
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
notificationPayloadFile = "SDWebImage Watch Demo Extension/PushNotificationPayload.apns">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/SDWebImage iOS Demo">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "43A629ED1D0E07600089D7DD"
@ -80,7 +78,7 @@
BlueprintName = "SDWebImage Watch Demo"
ReferencedContainer = "container:SDWebImage Demo.xcodeproj">
</BuildableReference>
</RemoteRunnable>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -88,10 +86,8 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.Carousel"
RemotePath = "/SDWebImage iOS Demo">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "43A629ED1D0E07600089D7DD"
@ -99,16 +95,7 @@
BlueprintName = "SDWebImage Watch Demo"
ReferencedContainer = "container:SDWebImage Demo.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "43A629ED1D0E07600089D7DD"
BuildableName = "SDWebImage Watch Demo.app"
BlueprintName = "SDWebImage Watch Demo"
ReferencedContainer = "container:SDWebImage Demo.xcodeproj">
</BuildableReference>
</MacroExpansion>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

View File

@ -498,9 +498,13 @@ static id<SDImageLoader> _defaultImageLoader;
}
id<SDWebImageCacheSerializer> cacheSerializer = context[SDWebImageContextCacheSerializer];
// transformer check
BOOL shouldTransformImage = downloadedImage && transformer;
shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage));
shouldTransformImage = shouldTransformImage && (!downloadedImage.sd_isVector || (options & SDWebImageTransformVectorImage));
// thumbnail check
BOOL shouldThumbnailImage = context[SDWebImageContextImageThumbnailPixelSize] != nil || downloadedImage.sd_decodeOptions[SDImageCoderDecodeThumbnailPixelSize] != nil;
BOOL shouldCacheOriginal = downloadedImage && finished && cacheType == SDImageCacheTypeNone;
// if available, store original image to cache
@ -508,27 +512,26 @@ static id<SDImageLoader> _defaultImageLoader;
// 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
SDImageCacheType targetStoreCacheType = shouldTransformImage ? originalStoreCacheType : storeCacheType;
UIImage *originalImage = downloadedImage;
BOOL thumbnailed = context[SDWebImageContextImageThumbnailPixelSize] != nil;
if (thumbnailed) {
SDImageCacheType targetStoreCacheType = (shouldTransformImage || shouldThumbnailImage) ? originalStoreCacheType : storeCacheType;
UIImage *fullSizeImage = downloadedImage;
if (shouldThumbnailImage) {
// 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;
fullSizeImage = nil;
}
if (originalImage && cacheSerializer && (targetStoreCacheType == SDImageCacheTypeDisk || targetStoreCacheType == SDImageCacheTypeAll)) {
if (fullSizeImage && cacheSerializer && (targetStoreCacheType == SDImageCacheTypeDisk || targetStoreCacheType == SDImageCacheTypeAll)) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@autoreleasepool {
NSData *cacheData = [cacheSerializer cacheDataWithImage:originalImage originalData:downloadedData imageURL:url];
[self storeImage:originalImage imageData:cacheData forKey:key imageCache:imageCache cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{
NSData *cacheData = [cacheSerializer cacheDataWithImage:fullSizeImage originalData:downloadedData imageURL:url];
[self storeImage:fullSizeImage imageData:cacheData forKey:key imageCache:imageCache cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{
// Continue transform process
[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData cacheType:cacheType finished:finished completed:completedBlock];
}];
}
});
} else {
[self storeImage:originalImage imageData:downloadedData forKey:key imageCache:imageCache cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{
[self storeImage:fullSizeImage imageData:downloadedData forKey:key imageCache:imageCache cacheType:targetStoreCacheType waitStoreCache:waitStoreCache completion:^{
// Continue transform process
[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData cacheType:cacheType finished:finished completed:completedBlock];
}];
@ -558,7 +561,6 @@ static id<SDImageLoader> _defaultImageLoader;
if (![transformer conformsToProtocol:@protocol(SDImageTransformer)]) {
transformer = nil;
}
id<SDWebImageCacheSerializer> cacheSerializer = context[SDWebImageContextCacheSerializer];
// transformer check
BOOL shouldTransformImage = originalImage && transformer;
@ -588,24 +590,24 @@ static id<SDImageLoader> _defaultImageLoader;
NSString *key = [self cacheKeyForURL:url context:context];
// Case that transformer one thumbnail, which this time need full pixel image
UIImage *fullSizeImage = originalImage;
BOOL imageWasRedecoded = NO;
if (shouldRedecodeFullImage) {
fullSizeImage = SDImageCacheDecodeImageData(originalData, key, options, context) ?: originalImage;
fullSizeImage = SDImageCacheDecodeImageData(originalData, key, options, context);
if (fullSizeImage) {
imageWasRedecoded = YES;
} else {
imageWasRedecoded = NO;
fullSizeImage = originalImage; // Fallback
}
}
UIImage *transformedImage = [transformer transformedImageWithImage:fullSizeImage forKey:key];
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:fullSizeImage];
NSData *cacheData;
// pass nil if the image was transformed, so we can recalculate the data from the image
if (cacheSerializer && (storeCacheType == SDImageCacheTypeDisk || storeCacheType == SDImageCacheTypeAll)) {
cacheData = [cacheSerializer cacheDataWithImage:transformedImage originalData:(imageWasTransformed ? nil : originalData) imageURL:url];
} else {
cacheData = (imageWasTransformed ? nil : originalData);
}
// Continue store transform cache process
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:transformedImage data:cacheData cacheType:cacheType transformed:imageWasTransformed finished:finished completed:completedBlock];
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:transformedImage data:originalData cacheType:cacheType finished:finished transformed:imageWasTransformed || imageWasRedecoded completed:completedBlock];
} else {
// Continue store transform cache process
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:fullSizeImage data:originalData cacheType:cacheType transformed:NO finished:finished completed:completedBlock];
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:fullSizeImage data:originalData cacheType:cacheType finished:finished transformed:imageWasRedecoded completed:completedBlock];
}
}
});
@ -613,14 +615,21 @@ static id<SDImageLoader> _defaultImageLoader;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@autoreleasepool {
// Re-decode because the returned image does not match current request pipeline's context
UIImage *fullSizeImage = SDImageCacheDecodeImageData(originalData, url.absoluteString, options, context) ?: originalImage;
UIImage *fullSizeImage = SDImageCacheDecodeImageData(originalData, url.absoluteString, options, context);
BOOL imageWasRedecoded = NO;
if (fullSizeImage) {
imageWasRedecoded = YES;
} else {
imageWasRedecoded = NO;
fullSizeImage = originalImage; // Fallback
}
// Continue store transform cache process
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:fullSizeImage data:originalData cacheType:cacheType transformed:NO finished:finished completed:completedBlock];
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:fullSizeImage data:originalData cacheType:cacheType finished:finished transformed:imageWasRedecoded completed:completedBlock];
}
});
} else {
// 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];
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:originalImage data:originalData cacheType:cacheType finished:finished transformed:NO completed:completedBlock];
}
}
@ -631,8 +640,8 @@ static id<SDImageLoader> _defaultImageLoader;
image:(nullable UIImage *)image
data:(nullable NSData *)data
cacheType:(SDImageCacheType)cacheType
transformed:(BOOL)transformed
finished:(BOOL)finished
transformed:(BOOL)transformed
completed:(nullable SDInternalCompletionBlock)completedBlock {
// Grab the image cache to use
id<SDImageCache> imageCache;
@ -647,18 +656,19 @@ static id<SDImageLoader> _defaultImageLoader;
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] != nil;
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) {
id<SDWebImageCacheSerializer> cacheSerializer = context[SDWebImageContextCacheSerializer];
// thumbnail check
BOOL shouldThumbnailImage = context[SDWebImageContextImageThumbnailPixelSize] != nil || image.sd_decodeOptions[SDImageCoderDecodeThumbnailPixelSize] != nil;
// Store the transformed/thumbnail image into the cache
if (transformed || shouldThumbnailImage) {
NSData *cacheData;
// pass nil if the image was transformed/thumbnailed, so we can recalculate the data from the image
if (cacheSerializer && (storeCacheType == SDImageCacheTypeDisk || storeCacheType == SDImageCacheTypeAll)) {
cacheData = [cacheSerializer cacheDataWithImage:image originalData:nil imageURL:url];
} else {
cacheData = nil;
}
// 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:^{