Quick fix the issue that UIImage.sd_decodeOptions cause retain cycle when pass custom cache in context option

Now the SDImageCoderWebImageContext does not pass the built-in options, only custom options will be passed, and deprecated in 5.14.0
This commit is contained in:
DreamPiggy 2022-10-29 19:50:45 +08:00
parent 9248fe561a
commit b46fe38c72
3 changed files with 49 additions and 1 deletions

View File

@ -13,6 +13,37 @@
#import "UIImage+Metadata.h" #import "UIImage+Metadata.h"
#import "SDInternalMacros.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) { SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext * _Nullable context, SDWebImageOptions options, NSString * _Nonnull cacheKey) {
BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly); BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly);
NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor]; NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor];
@ -34,7 +65,10 @@ SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext *
mutableCoderOptions[SDImageCoderDecodeScaleFactor] = @(scale); mutableCoderOptions[SDImageCoderDecodeScaleFactor] = @(scale);
mutableCoderOptions[SDImageCoderDecodePreserveAspectRatio] = preserveAspectRatioValue; mutableCoderOptions[SDImageCoderDecodePreserveAspectRatio] = preserveAspectRatioValue;
mutableCoderOptions[SDImageCoderDecodeThumbnailPixelSize] = thumbnailSizeValue; 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]; SDImageCoderOptions *coderOptions = [mutableCoderOptions copy];
return coderOptions; 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) 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. 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. 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. 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)); 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]; [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 { - (NSString *)testJPEGPath {
NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
return [testBundle pathForResource:@"TestImage" ofType:@"jpg"]; return [testBundle pathForResource:@"TestImage" ofType:@"jpg"];