From 37f84ce6a65e7598c9be9177295f82915ee236b6 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sun, 17 Dec 2017 03:04:59 +0800 Subject: [PATCH] Use a weak pointerArray to store the operations for sd_setAnimationImagesWithURLs, avoid extra retain of operation instance --- SDWebImage/UIImageView+WebCache.m | 36 ++++++++++++++++++++++++--- SDWebImage/UIView+WebCacheOperation.h | 2 +- SDWebImage/UIView+WebCacheOperation.m | 26 ++++++++----------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/SDWebImage/UIImageView+WebCache.m b/SDWebImage/UIImageView+WebCache.m index 5a7e7988..8832f2e4 100644 --- a/SDWebImage/UIImageView+WebCache.m +++ b/SDWebImage/UIImageView+WebCache.m @@ -73,7 +73,7 @@ [self sd_cancelCurrentAnimationImagesLoad]; __weak __typeof(self)wself = self; - NSMutableArray> *operationsArray = [[NSMutableArray alloc] init]; + NSPointerArray *operationsArray = [self sd_animationOperationArray]; [arrayOfURLs enumerateObjectsUsingBlock:^(NSURL *logoImageURL, NSUInteger idx, BOOL * _Nonnull stop) { id operation = [[SDWebImageManager sharedManager] loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { @@ -103,14 +103,42 @@ [sself startAnimating]; }); }]; - [operationsArray addObject:operation]; + @synchronized (self) { + [operationsArray addPointer:(__bridge void *)(operation)]; + } }]; +} - [self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"]; +static char animationloadOperationKey; + +// element is weak because operation instance is retained by SDWebImageManager's runningOperations property +// we should use lock to keep thread-safe because these method may not be acessed from main queue +- (NSPointerArray *)sd_animationOperationArray { + @synchronized(self) { + NSPointerArray *operationsArray = objc_getAssociatedObject(self, &animationloadOperationKey); + if (operationsArray) { + return operationsArray; + } + operationsArray = [NSPointerArray weakObjectsPointerArray]; + objc_setAssociatedObject(self, &animationloadOperationKey, operationsArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return operationsArray; + } } - (void)sd_cancelCurrentAnimationImagesLoad { - [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"]; + NSPointerArray *operationsArray = [self sd_animationOperationArray]; + if (operationsArray) { + @synchronized (self) { + for (id operation in operationsArray) { + if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) { + [operation cancel]; + } + } + for (size_t i = 0; i < operationsArray.count; i++) { + [operationsArray removePointerAtIndex:i]; + } + } + } } #endif diff --git a/SDWebImage/UIView+WebCacheOperation.h b/SDWebImage/UIView+WebCacheOperation.h index f5e95fa6..bef703eb 100644 --- a/SDWebImage/UIView+WebCacheOperation.h +++ b/SDWebImage/UIView+WebCacheOperation.h @@ -20,7 +20,7 @@ * @param operation the operation * @param key key for storing the operation */ -- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key; +- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key; /** * Cancel all operations for the current UIView and key diff --git a/SDWebImage/UIView+WebCacheOperation.m b/SDWebImage/UIView+WebCacheOperation.m index f05afec9..bcdaa3d1 100644 --- a/SDWebImage/UIView+WebCacheOperation.m +++ b/SDWebImage/UIView+WebCacheOperation.m @@ -20,7 +20,7 @@ typedef NSMapTable SDOperationsDictionary; @implementation UIView (WebCacheOperation) -- (SDOperationsDictionary *)operationDictionary { +- (SDOperationsDictionary *)sd_operationDictionary { @synchronized(self) { SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); if (operations) { @@ -32,11 +32,11 @@ typedef NSMapTable SDOperationsDictionary; } } -- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key { +- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key { if (key) { [self sd_cancelImageLoadOperationWithKey:key]; if (operation) { - SDOperationsDictionary *operationDictionary = [self operationDictionary]; + SDOperationsDictionary *operationDictionary = [self sd_operationDictionary]; @synchronized (self) { [operationDictionary setObject:operation forKey:key]; } @@ -46,20 +46,14 @@ typedef NSMapTable SDOperationsDictionary; - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key { // Cancel in progress downloader from queue - SDOperationsDictionary *operationDictionary = [self operationDictionary]; - id operations; + SDOperationsDictionary *operationDictionary = [self sd_operationDictionary]; + id operation; @synchronized (self) { - operations = [operationDictionary objectForKey:key]; + operation = [operationDictionary objectForKey:key]; } - if (operations) { - if ([operations isKindOfClass:[NSArray class]]) { - for (id operation in operations) { - if (operation) { - [operation cancel]; - } - } - } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){ - [(id) operations cancel]; + if (operation) { + if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]){ + [(id) operation cancel]; } @synchronized (self) { [operationDictionary removeObjectForKey:key]; @@ -69,7 +63,7 @@ typedef NSMapTable SDOperationsDictionary; - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key { if (key) { - SDOperationsDictionary *operationDictionary = [self operationDictionary]; + SDOperationsDictionary *operationDictionary = [self sd_operationDictionary]; @synchronized (self) { [operationDictionary removeObjectForKey:key]; }