From 4ed1ce29dcda580051750325d204af517409a88d Mon Sep 17 00:00:00 2001 From: kinarobin Date: Thu, 16 Jul 2020 17:33:08 +0800 Subject: [PATCH] fix race condition when use transition --- SDWebImage/Core/UIView+WebCache.h | 1 + SDWebImage/Core/UIView+WebCache.m | 19 +++++++++++++------ Tests/Tests/SDWebCacheCategoriesTests.m | 1 + 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/SDWebImage/Core/UIView+WebCache.h b/SDWebImage/Core/UIView+WebCache.h index f48c2f0b..ee9f761b 100644 --- a/SDWebImage/Core/UIView+WebCache.h +++ b/SDWebImage/Core/UIView+WebCache.h @@ -34,6 +34,7 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima /** * Get the current image operation key. Operation key is used to identify the different queries for one view instance (like UIButton). * See more about this in `SDWebImageContextSetImageOperationKey`. + * If you cancel current image load, the key will be set to nil. * @note You can use method `UIView+WebCacheOperation` to investigate different queries' operation. */ @property (nonatomic, strong, readonly, nullable) NSString *sd_latestOperationKey; diff --git a/SDWebImage/Core/UIView+WebCache.m b/SDWebImage/Core/UIView+WebCache.m index ca362fbf..fb8c76a1 100644 --- a/SDWebImage/Core/UIView+WebCache.m +++ b/SDWebImage/Core/UIView+WebCache.m @@ -203,6 +203,7 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; - (void)sd_cancelCurrentImageLoad { [self sd_cancelImageLoadOperationWithKey:self.sd_latestOperationKey]; + self.sd_latestOperationKey = nil; } - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL { @@ -228,14 +229,18 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; } else if ([view isKindOfClass:[UIImageView class]]) { UIImageView *imageView = (UIImageView *)view; finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData, SDImageCacheType setCacheType, NSURL *setImageURL) { - imageView.image = setImage; + if (view.sd_latestOperationKey != nil) { + imageView.image = setImage; + } }; } #if SD_UIKIT else if ([view isKindOfClass:[UIButton class]]) { UIButton *button = (UIButton *)view; finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData, SDImageCacheType setCacheType, NSURL *setImageURL) { - [button setImage:setImage forState:UIControlStateNormal]; + if (view.sd_latestOperationKey != nil) { + [button setImage:setImage forState:UIControlStateNormal]; + } }; } #endif @@ -243,7 +248,9 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; else if ([view isKindOfClass:[NSButton class]]) { NSButton *button = (NSButton *)view; finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData, SDImageCacheType setCacheType, NSURL *setImageURL) { - button.image = setImage; + if (view.sd_latestOperationKey != nil) { + button.image = setImage; + } }; } #endif @@ -275,10 +282,10 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL; } completionHandler:^{ [NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) { context.duration = transition.duration; - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" CAMediaTimingFunction *timingFunction = transition.timingFunction; - #pragma clang diagnostic pop +#pragma clang diagnostic pop if (!timingFunction) { timingFunction = SDTimingFunctionFromAnimationOptions(transition.animationOptions); } diff --git a/Tests/Tests/SDWebCacheCategoriesTests.m b/Tests/Tests/SDWebCacheCategoriesTests.m index 96fc7a3e..666037dd 100644 --- a/Tests/Tests/SDWebCacheCategoriesTests.m +++ b/Tests/Tests/SDWebCacheCategoriesTests.m @@ -208,6 +208,7 @@ [imageView sd_internalSetImageWithURL:originalImageURL placeholderImage:nil options:0 context:nil setImageBlock:nil progress:nil completed:nil]; [imageView sd_cancelCurrentImageLoad]; NSString *operationKey = NSStringFromClass(UIView.class); + expect(imageView.sd_latestOperationKey).beNil(); expect([imageView sd_imageLoadOperationForKey:operationKey]).beNil(); }