From 98d058a1ea053484bc4df447153654a0e4a70549 Mon Sep 17 00:00:00 2001 From: aasdsjk <1289647068@qq.com> Date: Thu, 14 Mar 2024 15:20:11 +0800 Subject: [PATCH] url is kind of NSString and shouldUseWeakMemoryCache is YES, APP will crash. (#3686) * NSString will crash * unit test * unit test opt --------- Co-authored-by: songjk --- SDWebImage/Core/UIView+WebCache.m | 12 +++++++ Tests/Tests/SDWebCacheCategoriesTests.m | 46 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/SDWebImage/Core/UIView+WebCache.m b/SDWebImage/Core/UIView+WebCache.m index f456b718..64437a2d 100644 --- a/SDWebImage/Core/UIView+WebCache.m +++ b/SDWebImage/Core/UIView+WebCache.m @@ -61,6 +61,18 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; setImageBlock:(nullable SDSetImageBlock)setImageBlock progress:(nullable SDImageLoaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock { + + // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, Xcode won't + // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. + // if url is NSString and shouldUseWeakMemoryCache is true, [cacheKeyForURL:context] will crash. just for a global protect. + if ([url isKindOfClass:NSString.class]) { + url = [NSURL URLWithString:(NSString *)url]; + } + // Prevents app crashing on argument type error like sending NSNull instead of NSURL + if (![url isKindOfClass:NSURL.class]) { + url = nil; + } + if (context) { // copy to avoid mutable object context = [context copy]; diff --git a/Tests/Tests/SDWebCacheCategoriesTests.m b/Tests/Tests/SDWebCacheCategoriesTests.m index a980401e..5034c314 100644 --- a/Tests/Tests/SDWebCacheCategoriesTests.m +++ b/Tests/Tests/SDWebCacheCategoriesTests.m @@ -620,6 +620,52 @@ [self waitForExpectationsWithCommonTimeout]; } +// test url is nil +- (void)testUIViewImageUrlForNilWorks { + XCTestExpectation *expectation = [self expectationWithDescription:@"Completion is called with url is nil"]; + UIImageView *imageView = [[UIImageView alloc] init]; + SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"Test"]; + cache.config.shouldUseWeakMemoryCache = YES; + SDWebImageManager *imageManager = [[SDWebImageManager alloc] initWithCache:cache loader:[SDWebImageDownloader sharedDownloader]]; + [imageView sd_setImageWithURL:nil placeholderImage:nil options:0 context:@{SDWebImageContextCustomManager:imageManager} progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { + expect(image).to.beNil(); + [expectation fulfill]; + }]; + [self waitForExpectationsWithCommonTimeout]; + +} + +// test url is NSString. +- (void)testUIViewImageUrlForStringWorks { + + XCTestExpectation *expectation = [self expectationWithDescription:@"Completion is called with url is NSString"]; + + UIImageView *imageView = [[UIImageView alloc] init]; + SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"Test"]; + cache.config.shouldUseWeakMemoryCache = YES; + SDWebImageManager *imageManager = [[SDWebImageManager alloc] initWithCache:cache loader:[SDWebImageDownloader sharedDownloader]]; + [imageView sd_setImageWithURL:kTestJPEGURL placeholderImage:nil options:0 context:@{SDWebImageContextCustomManager:imageManager} progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { + expect(image).notTo.beNil(); + [expectation fulfill]; + }]; + [self waitForExpectationsWithCommonTimeout]; +} + +// test url is NSURL +- (void)testUIViewImageUrlForNSURLWorks { + XCTestExpectation *expectation = [self expectationWithDescription:@"Completion is called with url is NSURL"]; + UIImageView *imageView = [[UIImageView alloc] init]; + SDImageCache *cache = [[SDImageCache alloc] initWithNamespace:@"Test"]; + cache.config.shouldUseWeakMemoryCache = YES; + SDWebImageManager *imageManager = [[SDWebImageManager alloc] initWithCache:cache loader:[SDWebImageDownloader sharedDownloader]]; + [imageView sd_setImageWithURL:[NSURL URLWithString:kTestJPEGURL] placeholderImage:nil options:0 context:@{SDWebImageContextCustomManager:imageManager} progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { + expect(image).notTo.beNil(); + [expectation fulfill]; + }]; + [self waitForExpectationsWithCommonTimeout]; + +} + #pragma mark - Helper - (NSString *)testJPEGPath {