From e9527b393afb093e9cba094eb5bad2bb4a327f69 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 16 May 2018 15:35:55 +0800 Subject: [PATCH 01/11] Add optimalFrameCacheSize && predrawingEnabled options for FLAnimatedImage. It makes users to set optimal frame cache size of FLAnimatedImage after image load. --- .../FLAnimatedImageView+WebCache.h | 14 ++++++++ .../FLAnimatedImageView+WebCache.m | 35 ++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h index 6d2788b8..f046f7a6 100644 --- a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h +++ b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h @@ -38,6 +38,20 @@ */ @interface FLAnimatedImageView (WebCache) +/** + * Optimal frame cache size of FLAnimatedImage during initializer. (1.0.11 version later) + * This value will help you set `optimalFrameCacheSize` arg of FLAnimatedImage initializer after image load. + * Defaults to 0. + */ +@property (nonatomic, assign) NSUInteger sd_optimalFrameCacheSize; + +/** + * Predrawing control of FLAnimatedImage during initializer. (1.0.11 version later) + * This value will help you set `predrawingEnabled` arg of FLAnimatedImage initializer after image load. + * Defaults to YES. + */ +@property (nonatomic, assign) BOOL sd_predrawingEnabled; + /** * Load the image at the given url (either from cache or download) and load it in this imageView. It works with both static and dynamic images * The download is asynchronous and cached. diff --git a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m index 773d4f0b..644591b6 100644 --- a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m +++ b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m @@ -29,6 +29,33 @@ @implementation FLAnimatedImageView (WebCache) +// These property based options will moved to `SDWebImageContext` in 5.x, to allow per-image-request level options instead of per-imageView-level options +- (NSUInteger)sd_optimalFrameCacheSize { + NSUInteger optimalFrameCacheSize = 0; + NSNumber *value = objc_getAssociatedObject(self, @selector(sd_optimalFrameCacheSize)); + if ([value isKindOfClass:[NSNumber class]]) { + optimalFrameCacheSize = value.unsignedShortValue; + } + return optimalFrameCacheSize; +} + +- (void)setSd_optimalFrameCacheSize:(NSUInteger)sd_optimalFrameCacheSize { + objc_setAssociatedObject(self, @selector(sd_optimalFrameCacheSize), @(sd_optimalFrameCacheSize), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)sd_predrawingEnabled { + BOOL predrawingEnabled = YES; + NSNumber *value = objc_getAssociatedObject(self, @selector(sd_predrawingEnabled)); + if ([value isKindOfClass:[NSNumber class]]) { + predrawingEnabled = value.boolValue; + } + return predrawingEnabled; +} + +- (void)setSd_predrawingEnabled:(BOOL)sd_predrawingEnabled { + objc_setAssociatedObject(self, @selector(sd_predrawingEnabled), @(sd_predrawingEnabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + - (void)sd_setImageWithURL:(nullable NSURL *)url { [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; } @@ -83,7 +110,13 @@ weakSelf.animatedImage = nil; // Secondly create FLAnimatedImage in global queue because it's time consuming, then set it back dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ - FLAnimatedImage *animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData]; + FLAnimatedImage *animatedImage; + // Compatibility in 4.x for lower version FLAnimatedImage. + if ([FLAnimatedImage respondsToSelector:@selector(initWithAnimatedGIFData:optimalFrameCacheSize:predrawingEnabled:)]) { + animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData optimalFrameCacheSize:self.sd_optimalFrameCacheSize predrawingEnabled:self.sd_predrawingEnabled]; + } else { + animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData]; + } dispatch_async(dispatch_get_main_queue(), ^{ image.sd_FLAnimatedImage = animatedImage; weakSelf.animatedImage = animatedImage; From 7515f06b4470d1b65ef471403acc2e5571e016c7 Mon Sep 17 00:00:00 2001 From: zhongwuzw Date: Tue, 22 May 2018 20:34:41 +0800 Subject: [PATCH 02/11] Minor optimize for dispatch_queue_async_safe --- SDWebImage/SDWebImageCompat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDWebImage/SDWebImageCompat.h b/SDWebImage/SDWebImageCompat.h index ce068071..20d03262 100644 --- a/SDWebImage/SDWebImageCompat.h +++ b/SDWebImage/SDWebImageCompat.h @@ -89,7 +89,7 @@ FOUNDATION_EXPORT NSString *const SDWebImageErrorDomain; #ifndef dispatch_queue_async_safe #define dispatch_queue_async_safe(queue, block)\ - if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(queue)) == 0) {\ + if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(queue)) {\ block();\ } else {\ dispatch_async(queue, block);\ From a6e3907f26db729c58ff5eb820e12e12a05bd7d5 Mon Sep 17 00:00:00 2001 From: Lizhen Hu Date: Mon, 28 May 2018 13:23:59 +0800 Subject: [PATCH 03/11] Replace @synchronized with pthread_mutex_t & dispatch_semaphore_t in SDWebImageManager --- SDWebImage/SDWebImageManager.m | 67 +++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index acfe6c77..f53d46d1 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -9,6 +9,10 @@ #import "SDWebImageManager.h" #import "NSImage+WebCache.h" #import +#import + +#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); +#define UNLOCK(lock) dispatch_semaphore_signal(lock); @interface SDWebImageCombinedOperation : NSObject @@ -24,11 +28,14 @@ @property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache; @property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader *imageDownloader; @property (strong, nonatomic, nonnull) NSMutableSet *failedURLs; +@property (strong, nonatomic, nonnull) dispatch_semaphore_t failedURLsLock; // a lock to keep the access to `failedURLs` thread-safe @property (strong, nonatomic, nonnull) NSMutableArray *runningOperations; @end -@implementation SDWebImageManager +@implementation SDWebImageManager { + pthread_mutex_t _runningOperationsLock; // a lock to keep the access to `runningOperations` thread-safe +} + (nonnull instancetype)sharedManager { static dispatch_once_t once; @@ -39,6 +46,10 @@ return instance; } +- (void)dealloc { + pthread_mutex_destroy(&_runningOperationsLock); +} + - (nonnull instancetype)init { SDImageCache *cache = [SDImageCache sharedImageCache]; SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; @@ -50,7 +61,13 @@ _imageCache = cache; _imageDownloader = downloader; _failedURLs = [NSMutableSet new]; + _failedURLsLock = dispatch_semaphore_create(1); _runningOperations = [NSMutableArray new]; + + pthread_mutexattr_t attribute; + pthread_mutexattr_init(&attribute); + pthread_mutexattr_settype(&attribute, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_runningOperationsLock, &attribute); } return self; } @@ -130,9 +147,9 @@ BOOL isFailedUrl = NO; if (url) { - @synchronized (self.failedURLs) { - isFailedUrl = [self.failedURLs containsObject:url]; - } + LOCK(self.failedURLsLock); + isFailedUrl = [self.failedURLs containsObject:url]; + UNLOCK(self.failedURLsLock); } if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { @@ -140,9 +157,9 @@ return operation; } - @synchronized (self.runningOperations) { - [self.runningOperations addObject:operation]; - } + pthread_mutex_lock(&_runningOperationsLock); + [self.runningOperations addObject:operation]; + pthread_mutex_unlock(&_runningOperationsLock); NSString *key = [self cacheKeyForURL:url]; SDImageCacheOptions cacheOptions = 0; @@ -213,16 +230,16 @@ } if (shouldBlockFailedURL) { - @synchronized (self.failedURLs) { - [self.failedURLs addObject:url]; - } + LOCK(self.failedURLsLock); + [self.failedURLs addObject:url]; + UNLOCK(self.failedURLsLock); } } else { if ((options & SDWebImageRetryFailed)) { - @synchronized (self.failedURLs) { - [self.failedURLs removeObject:url]; - } + LOCK(self.failedURLsLock); + [self.failedURLs removeObject:url]; + UNLOCK(self.failedURLsLock); } BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); @@ -292,27 +309,27 @@ } - (void)cancelAll { - @synchronized (self.runningOperations) { - NSArray *copiedOperations = [self.runningOperations copy]; - [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; - [self.runningOperations removeObjectsInArray:copiedOperations]; - } + pthread_mutex_lock(&_runningOperationsLock); + NSArray *copiedOperations = [self.runningOperations copy]; + [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; + [self.runningOperations removeObjectsInArray:copiedOperations]; + pthread_mutex_unlock(&_runningOperationsLock); } - (BOOL)isRunning { BOOL isRunning = NO; - @synchronized (self.runningOperations) { - isRunning = (self.runningOperations.count > 0); - } + pthread_mutex_lock(&_runningOperationsLock); + isRunning = (self.runningOperations.count > 0); + pthread_mutex_unlock(&_runningOperationsLock); return isRunning; } - (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation { - @synchronized (self.runningOperations) { - if (operation) { - [self.runningOperations removeObject:operation]; - } + pthread_mutex_lock(&_runningOperationsLock); + if (operation) { + [self.runningOperations removeObject:operation]; } + pthread_mutex_unlock(&_runningOperationsLock); } - (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation From 7dc38751e9d73333836df79bf84252ed2f178702 Mon Sep 17 00:00:00 2001 From: Lizhen Hu Date: Mon, 28 May 2018 23:10:40 +0800 Subject: [PATCH 04/11] Use non-recursive lock for runningOperations in SDWebImageManager --- SDWebImage/SDWebImageManager.m | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index f53d46d1..839be3a2 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -30,12 +30,11 @@ @property (strong, nonatomic, nonnull) NSMutableSet *failedURLs; @property (strong, nonatomic, nonnull) dispatch_semaphore_t failedURLsLock; // a lock to keep the access to `failedURLs` thread-safe @property (strong, nonatomic, nonnull) NSMutableArray *runningOperations; +@property (strong, nonatomic, nonnull) dispatch_semaphore_t runningOperationsLock; // a lock to keep the access to `runningOperations` thread-safe @end -@implementation SDWebImageManager { - pthread_mutex_t _runningOperationsLock; // a lock to keep the access to `runningOperations` thread-safe -} +@implementation SDWebImageManager + (nonnull instancetype)sharedManager { static dispatch_once_t once; @@ -46,10 +45,6 @@ return instance; } -- (void)dealloc { - pthread_mutex_destroy(&_runningOperationsLock); -} - - (nonnull instancetype)init { SDImageCache *cache = [SDImageCache sharedImageCache]; SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; @@ -63,11 +58,7 @@ _failedURLs = [NSMutableSet new]; _failedURLsLock = dispatch_semaphore_create(1); _runningOperations = [NSMutableArray new]; - - pthread_mutexattr_t attribute; - pthread_mutexattr_init(&attribute); - pthread_mutexattr_settype(&attribute, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_runningOperationsLock, &attribute); + _runningOperationsLock = dispatch_semaphore_create(1); } return self; } @@ -157,9 +148,9 @@ return operation; } - pthread_mutex_lock(&_runningOperationsLock); + LOCK(self.runningOperationsLock); [self.runningOperations addObject:operation]; - pthread_mutex_unlock(&_runningOperationsLock); + UNLOCK(self.runningOperationsLock); NSString *key = [self cacheKeyForURL:url]; SDImageCacheOptions cacheOptions = 0; @@ -309,27 +300,26 @@ } - (void)cancelAll { - pthread_mutex_lock(&_runningOperationsLock); + LOCK(self.runningOperationsLock); NSArray *copiedOperations = [self.runningOperations copy]; - [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; - [self.runningOperations removeObjectsInArray:copiedOperations]; - pthread_mutex_unlock(&_runningOperationsLock); + UNLOCK(self.runningOperationsLock); + [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; // This will call `safelyRemoveOperationFromRunning:` and remove from the array } - (BOOL)isRunning { BOOL isRunning = NO; - pthread_mutex_lock(&_runningOperationsLock); + LOCK(self.runningOperationsLock); isRunning = (self.runningOperations.count > 0); - pthread_mutex_unlock(&_runningOperationsLock); + UNLOCK(self.runningOperationsLock); return isRunning; } - (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation { - pthread_mutex_lock(&_runningOperationsLock); + LOCK(self.runningOperationsLock); if (operation) { [self.runningOperations removeObject:operation]; } - pthread_mutex_unlock(&_runningOperationsLock); + UNLOCK(self.runningOperationsLock); } - (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation From 09020fac87ac1274c99f7002991ca854bec1e62e Mon Sep 17 00:00:00 2001 From: Lizhen Hu Date: Mon, 28 May 2018 23:12:38 +0800 Subject: [PATCH 05/11] Replace @synchronized lock with dispatch_semaphore lock for SDWebImageCombinedOperation --- SDWebImage/SDWebImageManager.m | 36 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 839be3a2..8bd276ea 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -347,20 +347,34 @@ @end +@interface SDWebImageCombinedOperation () + +@property (strong, nonatomic, nonnull) dispatch_semaphore_t cancelLock; // a lock to make the `cancel` method thread-safe + +@end + @implementation SDWebImageCombinedOperation -- (void)cancel { - @synchronized(self) { - self.cancelled = YES; - if (self.cacheOperation) { - [self.cacheOperation cancel]; - self.cacheOperation = nil; - } - if (self.downloadToken) { - [self.manager.imageDownloader cancel:self.downloadToken]; - } - [self.manager safelyRemoveOperationFromRunning:self]; +- (instancetype)init { + self = [super init]; + if (self) { + _cancelLock = dispatch_semaphore_create(1); } + return self; +} + +- (void)cancel { + LOCK(self.cancelLock); + self.cancelled = YES; + if (self.cacheOperation) { + [self.cacheOperation cancel]; + self.cacheOperation = nil; + } + if (self.downloadToken) { + [self.manager.imageDownloader cancel:self.downloadToken]; + } + [self.manager safelyRemoveOperationFromRunning:self]; + UNLOCK(self.cancelLock); } @end From 1aced8aa62dde4b8dc344f926078433322b1b18a Mon Sep 17 00:00:00 2001 From: Lizhen Hu Date: Mon, 28 May 2018 23:26:09 +0800 Subject: [PATCH 06/11] Remove unused header file --- SDWebImage/SDWebImageManager.m | 1 - 1 file changed, 1 deletion(-) diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 8bd276ea..94c6fc3d 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -9,7 +9,6 @@ #import "SDWebImageManager.h" #import "NSImage+WebCache.h" #import -#import #define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); #define UNLOCK(lock) dispatch_semaphore_signal(lock); From 0b8207b1d27220a85241f501f66fff17db4a93cd Mon Sep 17 00:00:00 2001 From: Lizhen Hu Date: Wed, 30 May 2018 13:58:47 +0800 Subject: [PATCH 07/11] Refactor in SDWebImageManager --- SDWebImage/SDWebImageManager.m | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 94c6fc3d..0dfae0a8 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -314,10 +314,11 @@ } - (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation { - LOCK(self.runningOperationsLock); - if (operation) { - [self.runningOperations removeObject:operation]; + if (!operation) { + return; } + LOCK(self.runningOperationsLock); + [self.runningOperations removeObject:operation]; UNLOCK(self.runningOperationsLock); } From 3ee1b60678554f81a053af95a3b014b267400414 Mon Sep 17 00:00:00 2001 From: Lizhen Hu Date: Wed, 30 May 2018 14:57:41 +0800 Subject: [PATCH 08/11] Revert replacement of @synchronized in SDWebImageCombinedOperation --- SDWebImage/SDWebImageManager.m | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 0dfae0a8..fa529e74 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -347,34 +347,20 @@ @end -@interface SDWebImageCombinedOperation () - -@property (strong, nonatomic, nonnull) dispatch_semaphore_t cancelLock; // a lock to make the `cancel` method thread-safe - -@end - @implementation SDWebImageCombinedOperation -- (instancetype)init { - self = [super init]; - if (self) { - _cancelLock = dispatch_semaphore_create(1); - } - return self; -} - - (void)cancel { - LOCK(self.cancelLock); - self.cancelled = YES; - if (self.cacheOperation) { - [self.cacheOperation cancel]; - self.cacheOperation = nil; + @synchronized(self) { + self.cancelled = YES; + if (self.cacheOperation) { + [self.cacheOperation cancel]; + self.cacheOperation = nil; + } + if (self.downloadToken) { + [self.manager.imageDownloader cancel:self.downloadToken]; + } + [self.manager safelyRemoveOperationFromRunning:self]; } - if (self.downloadToken) { - [self.manager.imageDownloader cancel:self.downloadToken]; - } - [self.manager safelyRemoveOperationFromRunning:self]; - UNLOCK(self.cancelLock); } @end From 799ef18c06dff086358f384dfeac63dad0755519 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 31 May 2018 11:32:52 +0800 Subject: [PATCH 09/11] Add the support for watchOS to use View Category method (sd_setImageWithURL:) on WKInterfaceImage --- .../InterfaceController.m | 7 +-- SDWebImage/SDWebImageCompat.h | 6 ++ SDWebImage/UIImageView+WebCache.h | 5 -- SDWebImage/UIImageView+WebCache.m | 5 -- SDWebImage/UIView+WebCache.h | 9 ++- SDWebImage/UIView+WebCache.m | 58 +++++++++++++------ SDWebImage/UIView+WebCacheOperation.h | 7 +-- SDWebImage/UIView+WebCacheOperation.m | 5 -- 8 files changed, 53 insertions(+), 49 deletions(-) diff --git a/Examples/SDWebImage Watch Demo Extension/InterfaceController.m b/Examples/SDWebImage Watch Demo Extension/InterfaceController.m index d36cec8d..f10193b6 100644 --- a/Examples/SDWebImage Watch Demo Extension/InterfaceController.m +++ b/Examples/SDWebImage Watch Demo Extension/InterfaceController.m @@ -7,7 +7,7 @@ */ #import "InterfaceController.h" -#import +#import @interface InterfaceController() @@ -30,9 +30,8 @@ [super willActivate]; NSString *urlString = @"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png"; - [[SDWebImageManager sharedManager] loadImageWithURL:[NSURL URLWithString:urlString] options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) { - self.imageInterface.image = image; - }]; + WKInterfaceImage *imageInterface = self.imageInterface; + [imageInterface sd_setImageWithURL:[NSURL URLWithString:urlString]]; } - (void)didDeactivate { diff --git a/SDWebImage/SDWebImageCompat.h b/SDWebImage/SDWebImageCompat.h index 20d03262..a4493aeb 100644 --- a/SDWebImage/SDWebImageCompat.h +++ b/SDWebImage/SDWebImageCompat.h @@ -70,6 +70,12 @@ #endif #if SD_WATCH #import + #ifndef UIView + #define UIView WKInterfaceObject + #endif + #ifndef UIImageView + #define UIImageView WKInterfaceImage + #endif #endif #endif diff --git a/SDWebImage/UIImageView+WebCache.h b/SDWebImage/UIImageView+WebCache.h index ef2eacfd..412cca0a 100644 --- a/SDWebImage/UIImageView+WebCache.h +++ b/SDWebImage/UIImageView+WebCache.h @@ -7,9 +7,6 @@ */ #import "SDWebImageCompat.h" - -#if SD_UIKIT || SD_MAC - #import "SDWebImageManager.h" /** @@ -195,5 +192,3 @@ #endif @end - -#endif diff --git a/SDWebImage/UIImageView+WebCache.m b/SDWebImage/UIImageView+WebCache.m index aa8ce431..1baea5d9 100644 --- a/SDWebImage/UIImageView+WebCache.m +++ b/SDWebImage/UIImageView+WebCache.m @@ -7,9 +7,6 @@ */ #import "UIImageView+WebCache.h" - -#if SD_UIKIT || SD_MAC - #import "objc/runtime.h" #import "UIView+WebCacheOperation.h" #import "UIView+WebCache.h" @@ -140,5 +137,3 @@ static char animationLoadOperationKey; #endif @end - -#endif diff --git a/SDWebImage/UIView+WebCache.h b/SDWebImage/UIView+WebCache.h index d89452d8..85cb2c6d 100644 --- a/SDWebImage/UIView+WebCache.h +++ b/SDWebImage/UIView+WebCache.h @@ -7,9 +7,6 @@ */ #import "SDWebImageCompat.h" - -#if SD_UIKIT || SD_MAC - #import "SDWebImageManager.h" #import "SDWebImageTransition.h" @@ -105,6 +102,8 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima */ - (void)sd_cancelCurrentImageLoad; +#if SD_UIKIT || SD_MAC + #pragma mark - Image Transition /** @@ -135,6 +134,6 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima #endif -@end - #endif + +@end diff --git a/SDWebImage/UIView+WebCache.m b/SDWebImage/UIView+WebCache.m index e9f6dc68..8193cda6 100644 --- a/SDWebImage/UIView+WebCache.m +++ b/SDWebImage/UIView+WebCache.m @@ -7,9 +7,6 @@ */ #import "UIView+WebCache.h" - -#if SD_UIKIT || SD_MAC - #import "objc/runtime.h" #import "UIView+WebCacheOperation.h" @@ -23,8 +20,8 @@ static char imageURLKey; #if SD_UIKIT static char TAG_ACTIVITY_INDICATOR; static char TAG_ACTIVITY_STYLE; -#endif static char TAG_ACTIVITY_SHOW; +#endif @implementation UIView (WebCache) @@ -78,10 +75,12 @@ static char TAG_ACTIVITY_SHOW; } if (url) { +#if SD_UIKIT // check if activityView is enabled or not if ([self sd_showActivityIndicatorView]) { [self sd_addActivityIndicator]; } +#endif // reset the progress self.sd_imageProgress.totalUnitCount = 0; @@ -105,7 +104,9 @@ static char TAG_ACTIVITY_SHOW; id operation = [manager loadImageWithURL:url options:options progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { __strong __typeof (wself) sself = wself; if (!sself) { return; } +#if SD_UIKIT [sself sd_removeActivityIndicator]; +#endif // if the progress not been updated, mark it to complete state if (finished && !error && sself.sd_imageProgress.totalUnitCount == 0 && sself.sd_imageProgress.completedUnitCount == 0) { sself.sd_imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown; @@ -144,16 +145,22 @@ static char TAG_ACTIVITY_SHOW; targetData = nil; } +#if SD_UIKIT || SD_MAC // check whether we should use the image transition SDWebImageTransition *transition = nil; if (finished && (options & SDWebImageForceTransition || cacheType == SDImageCacheTypeNone)) { transition = sself.sd_imageTransition; } +#endif if ([context valueForKey:SDWebImageInternalSetImageGroupKey]) { dispatch_group_t group = [context valueForKey:SDWebImageInternalSetImageGroupKey]; dispatch_group_enter(group); dispatch_main_async_safe(^{ +#if SD_UIKIT || SD_MAC [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL]; +#else + [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock]; +#endif }); // ensure completion block is called after custom setImage process finish dispatch_group_notify(group, dispatch_get_main_queue(), ^{ @@ -161,7 +168,11 @@ static char TAG_ACTIVITY_SHOW; }); } else { dispatch_main_async_safe(^{ +#if SD_UIKIT || SD_MAC [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL]; +#else + [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock]; +#endif callCompletedBlockClojure(); }); } @@ -169,7 +180,9 @@ static char TAG_ACTIVITY_SHOW; [self sd_setImageLoadOperation:operation forKey:validOperationKey]; } else { dispatch_main_async_safe(^{ +#if SD_UIKIT [self sd_removeActivityIndicator]; +#endif if (completedBlock) { NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; completedBlock(nil, error, SDImageCacheTypeNone, url); @@ -183,23 +196,31 @@ static char TAG_ACTIVITY_SHOW; } - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock { +#if SD_UIKIT || SD_MAC [self sd_setImage:image imageData:imageData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:nil cacheType:0 imageURL:nil]; +#else + // watchOS does not support view transition. Simplify the logic + if (setImageBlock) { + setImageBlock(image, imageData); + } else if ([self isKindOfClass:[UIImageView class]]) { + UIImageView *imageView = (UIImageView *)self; + [imageView setImage:image]; + } +#endif } +#if SD_UIKIT || SD_MAC - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock transition:(SDWebImageTransition *)transition cacheType:(SDImageCacheType)cacheType imageURL:(NSURL *)imageURL { UIView *view = self; SDSetImageBlock finalSetImageBlock; if (setImageBlock) { finalSetImageBlock = setImageBlock; - } -#if SD_UIKIT || SD_MAC - else if ([view isKindOfClass:[UIImageView class]]) { + } else if ([view isKindOfClass:[UIImageView class]]) { UIImageView *imageView = (UIImageView *)view; finalSetImageBlock = ^(UIImage *setImage, NSData *setImageData) { imageView.image = setImage; }; } -#endif #if SD_UIKIT else if ([view isKindOfClass:[UIButton class]]) { UIButton *button = (UIButton *)view; @@ -257,15 +278,20 @@ static char TAG_ACTIVITY_SHOW; } } } +#endif - (void)sd_setNeedsLayout { #if SD_UIKIT [self setNeedsLayout]; #elif SD_MAC [self setNeedsLayout:YES]; +#elif SD_WATCH + // Do nothing because WatchKit automatically layout the view after property change #endif } +#if SD_UIKIT || SD_MAC + #pragma mark - Image Transition - (SDWebImageTransition *)sd_imageTransition { return objc_getAssociatedObject(self, @selector(sd_imageTransition)); @@ -275,10 +301,9 @@ static char TAG_ACTIVITY_SHOW; objc_setAssociatedObject(self, @selector(sd_imageTransition), sd_imageTransition, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -#pragma mark - Activity indicator - -#pragma mark - #if SD_UIKIT + +#pragma mark - Activity indicator - (UIActivityIndicatorView *)activityIndicator { return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR); } @@ -286,7 +311,6 @@ static char TAG_ACTIVITY_SHOW; - (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator { objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN); } -#endif - (void)sd_setShowActivityIndicatorView:(BOOL)show { objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN); @@ -296,7 +320,6 @@ static char TAG_ACTIVITY_SHOW; return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue]; } -#if SD_UIKIT - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style{ objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN); } @@ -304,10 +327,8 @@ static char TAG_ACTIVITY_SHOW; - (int)sd_getIndicatorStyle{ return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue]; } -#endif - (void)sd_addActivityIndicator { -#if SD_UIKIT dispatch_main_async_safe(^{ if (!self.activityIndicator) { self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self sd_getIndicatorStyle]]; @@ -332,20 +353,19 @@ static char TAG_ACTIVITY_SHOW; } [self.activityIndicator startAnimating]; }); -#endif } - (void)sd_removeActivityIndicator { -#if SD_UIKIT dispatch_main_async_safe(^{ if (self.activityIndicator) { [self.activityIndicator removeFromSuperview]; self.activityIndicator = nil; } }); -#endif } -@end +#endif #endif + +@end diff --git a/SDWebImage/UIView+WebCacheOperation.h b/SDWebImage/UIView+WebCacheOperation.h index 5d44691f..0be284b6 100644 --- a/SDWebImage/UIView+WebCacheOperation.h +++ b/SDWebImage/UIView+WebCacheOperation.h @@ -7,10 +7,7 @@ */ #import "SDWebImageCompat.h" - -#if SD_UIKIT || SD_MAC - -#import "SDWebImageManager.h" +#import "SDWebImageOperation.h" // These methods are used to support canceling for UIView image loading, it's designed to be used internal but not external. // All the stored operations are weak, so it will be dalloced after image loading finished. If you need to store operations, use your own class to keep a strong reference for them. @@ -39,5 +36,3 @@ - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key; @end - -#endif diff --git a/SDWebImage/UIView+WebCacheOperation.m b/SDWebImage/UIView+WebCacheOperation.m index 3d1329cf..19d8b56a 100644 --- a/SDWebImage/UIView+WebCacheOperation.m +++ b/SDWebImage/UIView+WebCacheOperation.m @@ -7,9 +7,6 @@ */ #import "UIView+WebCacheOperation.h" - -#if SD_UIKIT || SD_MAC - #import "objc/runtime.h" static char loadOperationKey; @@ -71,5 +68,3 @@ typedef NSMapTable> SDOperationsDictionary; } @end - -#endif From 5062a7653e3233ff1195ce899e4304fd9efae6df Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 31 May 2018 17:39:02 +0800 Subject: [PATCH 10/11] Bumped version to 4.4.0 update CHANGELOG --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ SDWebImage.podspec | 2 +- WebImage/Info.plist | 4 ++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82e062e1..454fabd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +## [4.4.0 - watchOS View Category, on May 31st, 2018](https://github.com/rs/SDWebImage/releases/tag/4.4.0) +See [all tickets marked for the 4.4.0 release](https://github.com/rs/SDWebImage/milestone/25) + +#### Features +- View Category + - Add the support for watchOS to use View Category method (`sd_setImageWithURL:`) on WKInterfaceImage #2343 + - Add optimalFrameCacheSize && predrawingEnabled options for FLAnimatedImage #2323 +- Cache + - Add `SDImageCacheScaleDownLargeImages` to allow cache to scale down large images if need #2281 #2273 + +#### Improvements +- View Category + - Add `UIViewAnimationOptionAllowUserInteraction` as default options for convenient image transition #2315 +- Manager + - Replace `@synchronized` with dispatch_semaphore_t in SDWebImageManager #2340 + +#### Performances +- Coder + - Remove the extra calculation of image orientation for ImageIO coder #2313 + - Remove the duplicated process to force decode (draw on bitmap context) in Image/IO's progressive decoding #2314 +- Common + - Minor optimize for dispatch_queue_async_safe #2329 + +#### Fixes +- Coder + - Fix that force decode not works for alpha-channel images #2272 #2297 + - Fix WebP Encoding only works for RGBA8888 CGImage but not other color mode #2318 + - Fix the thread-safe issue for coders manager #2274 #2249 #1484 + - Fix the wrong declaration of NSArray generics #2260 + +#### Docs +- Fix function storeImageDataToDisk description #2301 + ## [4.3.3 - Cache Serializer, on Mar 12th, 2018](https://github.com/rs/SDWebImage/releases/tag/4.3.3) See [all tickets marked for the 4.3.3 release](https://github.com/rs/SDWebImage/milestone/24) @@ -631,3 +664,5 @@ For consistency, added async methods in `SDWebImageManager` `cachedImageExistsFo ## [1.0.0 on Dec 31st, 2009](https://github.com/rs/SDWebImage/releases/tag/1.0.0) ## [1.0 on Dec 31st, 2009](https://github.com/rs/SDWebImage/releases/tag/1.0) + + diff --git a/SDWebImage.podspec b/SDWebImage.podspec index b56cfe8d..3aa2d9e8 100644 --- a/SDWebImage.podspec +++ b/SDWebImage.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SDWebImage' - s.version = '4.3.3' + s.version = '4.4.0' s.osx.deployment_target = '10.9' s.ios.deployment_target = '7.0' diff --git a/WebImage/Info.plist b/WebImage/Info.plist index 1f3f55d0..d29940a0 100644 --- a/WebImage/Info.plist +++ b/WebImage/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.3.3 + 4.4.0 CFBundleSignature ???? CFBundleVersion - 4.3.3 + 4.4.0 NSPrincipalClass From 48acb8e4233f92bc4db07fc70815b09f0f917515 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Mon, 4 Jun 2018 20:16:27 +0800 Subject: [PATCH 11/11] Fix that the downloader operation may not call the completion block in race condition, which the operation we get is finished but not been removed from the operation array. --- SDWebImage/SDWebImageDownloader.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SDWebImage/SDWebImageDownloader.m b/SDWebImage/SDWebImageDownloader.m index f775a249..76291a17 100644 --- a/SDWebImage/SDWebImageDownloader.m +++ b/SDWebImage/SDWebImageDownloader.m @@ -275,7 +275,8 @@ LOCK(self.operationsLock); SDWebImageDownloaderOperation *operation = [self.URLOperations objectForKey:url]; - if (!operation) { + // There is a case that the operation may be marked as finished, but not been removed from `self.URLOperations`. + if (!operation || operation.isFinished) { operation = createCallback(); __weak typeof(self) wself = self; operation.completionBlock = ^{