Merge pull request #3053 from kinarobin/fix-race-condition-when-using-transition

Fix race condition when use transition but canceling an operation
This commit is contained in:
DreamPiggy 2020-07-16 21:27:51 +08:00 committed by GitHub
commit 19ca248e93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 29 additions and 4 deletions

View File

@ -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;

View File

@ -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 {
@ -251,22 +252,38 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
if (transition) {
#if SD_UIKIT
[UIView transitionWithView:view duration:0 options:0 animations:^{
if (!view.sd_latestOperationKey) {
return;
}
// 0 duration to let UIKit render placeholder and prepares block
if (transition.prepares) {
transition.prepares(view, image, imageData, cacheType, imageURL);
}
} completion:^(BOOL finished) {
[UIView transitionWithView:view duration:transition.duration options:transition.animationOptions animations:^{
if (!view.sd_latestOperationKey) {
return;
}
if (finalSetImageBlock && !transition.avoidAutoSetImage) {
finalSetImageBlock(image, imageData, cacheType, imageURL);
}
if (transition.animations) {
transition.animations(view, image);
}
} completion:transition.completion];
} completion:^(BOOL finished) {
if (!view.sd_latestOperationKey) {
return;
}
if (transition.completion) {
transition.completion(finished);
}
}];
}];
#elif SD_MAC
[NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull prepareContext) {
if (!view.sd_latestOperationKey) {
return;
}
// 0 duration to let AppKit render placeholder and prepares block
prepareContext.duration = 0;
if (transition.prepares) {
@ -274,11 +291,14 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
}
} completionHandler:^{
[NSAnimationContext runAnimationGroup:^(NSAnimationContext * _Nonnull context) {
if (!view.sd_latestOperationKey) {
return;
}
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);
}
@ -295,6 +315,9 @@ const int64_t SDWebImageProgressUnitCountUnknown = 1LL;
transition.animations(view, image);
}
} completionHandler:^{
if (!view.sd_latestOperationKey) {
return;
}
if (transition.completion) {
transition.completion(YES);
}

View File

@ -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();
}