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 470015bd..05db81bd 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.10' s.ios.deployment_target = '8.0' 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 2be6b024..51833df0 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]; } @@ -95,7 +122,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; diff --git a/SDWebImage/SDWebImageDownloader.m b/SDWebImage/SDWebImageDownloader.m index bdfa21bf..b107c2f7 100644 --- a/SDWebImage/SDWebImageDownloader.m +++ b/SDWebImage/SDWebImageDownloader.m @@ -253,7 +253,8 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext; LOCK(self.operationsLock); NSOperation *operation = [self.URLOperations objectForKey:url]; - if (!operation) { + if (!operation || operation.isFinished) { + // There is a case that the operation may be marked as finished, but not been removed from `self.URLOperations`. operation = createCallback(); if (!operation) { UNLOCK(self.operationsLock); diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 95d99865..6d1f9d0d 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -13,6 +13,9 @@ #import "SDAnimatedImage.h" #import "SDWebImageError.h" +#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); +#define UNLOCK(lock) dispatch_semaphore_signal(lock); + static id _defaultImageCache; static id _defaultImageLoader; @@ -30,7 +33,9 @@ static id _defaultImageLoader; @property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache; @property (strong, nonatomic, readwrite, nonnull) id imageLoader; @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 @@ -84,7 +89,9 @@ static id _defaultImageLoader; _imageCache = cache; _imageLoader = loader; _failedURLs = [NSMutableSet new]; + _failedURLsLock = dispatch_semaphore_create(1); _runningOperations = [NSMutableArray new]; + _runningOperationsLock = dispatch_semaphore_create(1); } return self; } @@ -137,9 +144,9 @@ static id _defaultImageLoader; 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)) { @@ -147,9 +154,9 @@ static id _defaultImageLoader; return operation; } - @synchronized (self.runningOperations) { - [self.runningOperations addObject:operation]; - } + LOCK(self.runningOperationsLock); + [self.runningOperations addObject:operation]; + UNLOCK(self.runningOperationsLock); // Preprocess the context arg to provide the default value from manager context = [self processedContextWithContext:context]; @@ -161,18 +168,17 @@ static id _defaultImageLoader; } - (void)cancelAll { - @synchronized (self.runningOperations) { - NSArray *copiedOperations = [self.runningOperations copy]; - [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; - [self.runningOperations removeObjectsInArray:copiedOperations]; - } + LOCK(self.runningOperationsLock); + NSArray *copiedOperations = [self.runningOperations copy]; + UNLOCK(self.runningOperationsLock); + [copiedOperations makeObjectsPerformSelector:@selector(cancel)]; // This will call `safelyRemoveOperationFromRunning:` and remove from the array } - (BOOL)isRunning { BOOL isRunning = NO; - @synchronized (self.runningOperations) { - isRunning = (self.runningOperations.count > 0); - } + LOCK(self.runningOperationsLock); + isRunning = (self.runningOperations.count > 0); + UNLOCK(self.runningOperationsLock); return isRunning; } @@ -263,15 +269,15 @@ static id _defaultImageLoader; } 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); } SDImageCacheType storeCacheType = SDImageCacheTypeAll; @@ -333,11 +339,12 @@ static id _defaultImageLoader; #pragma mark - Helper - (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation { - @synchronized (self.runningOperations) { - if (operation) { - [self.runningOperations removeObject:operation]; - } + if (!operation) { + return; } + LOCK(self.runningOperationsLock); + [self.runningOperations removeObject:operation]; + UNLOCK(self.runningOperationsLock); } - (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation 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