Merge pull request #3420 from dreampiggy/fix_5.13_retain_cycle_web_context

Quick fix the issue that UIImage.sd_decodeOptions cause retain cycle when pass custom cache in context option
This commit is contained in:
DreamPiggy 2022-10-29 20:22:21 +08:00 committed by GitHub
commit 34f2a9b823
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 1 deletions

View File

@ -13,6 +13,37 @@
#import "UIImage+Metadata.h"
#import "SDInternalMacros.h"
static NSArray<NSString *>* GetKnownContextOptions(void) {
static NSArray<NSString *> *knownContextOptions;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
knownContextOptions =
[NSArray arrayWithObjects:
SDWebImageContextSetImageOperationKey,
SDWebImageContextCustomManager,
SDWebImageContextImageCache,
SDWebImageContextImageLoader,
SDWebImageContextImageCoder,
SDWebImageContextImageTransformer,
SDWebImageContextImageScaleFactor,
SDWebImageContextImagePreserveAspectRatio,
SDWebImageContextImageThumbnailPixelSize,
SDWebImageContextQueryCacheType,
SDWebImageContextStoreCacheType,
SDWebImageContextOriginalQueryCacheType,
SDWebImageContextOriginalStoreCacheType,
SDWebImageContextOriginalImageCache,
SDWebImageContextAnimatedImageClass,
SDWebImageContextDownloadRequestModifier,
SDWebImageContextDownloadResponseModifier,
SDWebImageContextDownloadDecryptor,
SDWebImageContextCacheKeyFilter,
SDWebImageContextCacheSerializer
, nil];
});
return knownContextOptions;
}
SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext * _Nullable context, SDWebImageOptions options, NSString * _Nonnull cacheKey) {
BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly);
NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor];
@ -34,7 +65,10 @@ SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext *
mutableCoderOptions[SDImageCoderDecodeScaleFactor] = @(scale);
mutableCoderOptions[SDImageCoderDecodePreserveAspectRatio] = preserveAspectRatioValue;
mutableCoderOptions[SDImageCoderDecodeThumbnailPixelSize] = thumbnailSizeValue;
mutableCoderOptions[SDImageCoderWebImageContext] = context;
// Hack to remove all known context options before SDWebImage 5.14.0
SDImageCoderMutableOptions *mutableContext = [NSMutableDictionary dictionaryWithDictionary:context];
[mutableContext removeObjectsForKeys:GetKnownContextOptions()];
mutableCoderOptions[SDImageCoderWebImageContext] = [mutableContext copy];
SDImageCoderOptions *coderOptions = [mutableCoderOptions copy];
return coderOptions;

View File

@ -91,7 +91,9 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeEmbedThumb
A SDWebImageContext object which hold the original context options from top-level API. (SDWebImageContext)
This option is ignored for all built-in coders and take no effect.
But this may be useful for some custom coders, because some business logic may dependent on things other than image or image data information only.
Only the unknown context from top-level API (See SDWebImageDefine.h) may be passed in during image loading.
See `SDWebImageContext` for more detailed information.
@warning This option will be removed in 5.14.0
*/
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));

View File

@ -538,6 +538,18 @@
[self waitForExpectationsWithTimeout:kAsyncTestTimeout * 5 handler:nil];
}
- (void)test20ThatContextPassedToLoaderDoesNotContainsBuiltIn {
XCTestExpectation *expectation = [self expectationWithDescription:@"The SDImageCoderWebImageContext should contains only unknown context to avoid retain cycle"];
NSURL *url = [NSURL URLWithString:@"http://via.placeholder.com/502x502.png"];
[SDWebImageManager.sharedManager loadImageWithURL:url options:0 context:@{SDWebImageContextImageScaleFactor : @(2), @"Foo": @"Bar"} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
SDImageCoderOptions *decodeOptions = image.sd_decodeOptions;
SDWebImageContext *retrievedContext = decodeOptions[SDImageCoderWebImageContext];
expect(retrievedContext[@"Foo"]).equal(@"Bar");
[expectation fulfill];
}];
[self waitForExpectationsWithCommonTimeout];
}
- (NSString *)testJPEGPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
return [testBundle pathForResource:@"TestImage" ofType:@"jpg"];