diff --git a/CHANGELOG.md b/CHANGELOG.md index cc6ab455..518989e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +## [4.3.1 - 4.3.0 Patch](https://github.com/rs/SDWebImage/releases/tag/4.3.1) +See [all tickets marked for the 4.3.1 release](https://github.com/rs/SDWebImage/milestone/22) + +#### Fixes +- Cache + - Fix that SDImageCacheQueryDataWhenInMemory should response cacheType to memory cache when the in-memory cache hit #2218 +- Project + - Fix the macOS wrong minimum deployment target version to 10.9 #2206 + +#### Performances +- Download Operation + - Decode the image in the operation level's queue instead of URLSession delegate queue #2199 + +#### Improvements +- Coder + - Create a subclass of NSBitmapImageRep to fix the GIF frame duration issue on macOS #2223 +- View Category + - Expose the read write to FLAnimatedImage associate to the UIImage to allow advanced feature like placeholder #2220 +- Cache + - Create a subclass of NSCache using a weak cache #2228 +- Download Operation + - Improvement download operation for priority and some protect #2208 +- Project + - Fix CLANG\_WARN\_OBJC\_IMPLICIT\_RETAIN\_SELF warning #2225 + + ## [4.3.0 - Image Progress & Transition, on Jan 31th, 2018](https://github.com/rs/SDWebImage/releases/tag/4.3.0) See [all tickets marked for the 4.3.0 release](https://github.com/rs/SDWebImage/milestone/21) @@ -20,7 +46,7 @@ See [all tickets marked for the 4.3.0 release](https://github.com/rs/SDWebImage/ - Cache - Fix the getSize method which use the default file manager instead of current file manager #2180 - Manager - - Fix the leak of runningOperations in race condition #2177 (Manager) + - Fix the leak of runningOperations on race condition #2177 - Downloader - Ensure all the session delegate completionHandler called and fix the leak when response error code below iOS 10 #2197 - Fix dispatch_sync blocking the main queue on race condition #2184 @@ -31,9 +57,9 @@ See [all tickets marked for the 4.3.0 release](https://github.com/rs/SDWebImage/ - Prefetcher - Fix the issue that prefetcher will cause stack overflow when the input urls list is huge because of recursion function call #2196 -#### Performance +#### Performances - View Category - - Use the associate object to store the FLAnimatedImage into memory cache, avoid blinking or UIView transaction #2181 + - Use the associate object to store the FLAnimatedImage into memory cache, avoid blinking or UIView transition #2181 #### Improvements - Cache diff --git a/SDWebImage.podspec b/SDWebImage.podspec index 72a717d0..1422b1e1 100644 --- a/SDWebImage.podspec +++ b/SDWebImage.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SDWebImage' - s.version = '4.3.0' + s.version = '4.3.1' s.osx.deployment_target = '10.10' s.ios.deployment_target = '8.0' diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index ea9f4ef8..aab92873 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -11,6 +11,9 @@ #import "NSImage+Additions.h" #import "SDWebImageCodersManager.h" +#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); +#define UNLOCK(lock) dispatch_semaphore_signal(lock); + static void * SDImageCacheContext = &SDImageCacheContext; FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -21,10 +24,106 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { #endif } +// A memory cache which auto purge the cache on memory warning and support weak cache. +@interface SDMemoryCache : NSCache + +@end + +// Private +@interface SDMemoryCache () + +@property (nonatomic, strong, nonnull) NSMapTable *weakCache; // strong-weak cache +@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock; // a lock to keep the access to `weakCache` thread-safe + +@end + +@implementation SDMemoryCache + +// Current this seems no use on macOS (macOS use virtual memory and do not clear cache when memory warning). So we only override on iOS/tvOS platform. +// But in the future there may be more options and features for this subclass. +#if SD_UIKIT + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; +} + +- (instancetype)init { + self = [super init]; + if (self) { + // Use a strong-weak maptable storing the secondary cache. Follow the doc that NSCache does not copy keys + // This is useful when the memory warning, the cache was purged. However, the image instance can be retained by other instance such as imageViews and alive. + // At this case, we can sync weak cache back and do not need to load from disk cache + self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0]; + self.weakCacheLock = dispatch_semaphore_create(1); + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didReceiveMemoryWarning:) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + } + return self; +} + +- (void)didReceiveMemoryWarning:(NSNotification *)notification { + // Only remove cache, but keep weak cache + [super removeAllObjects]; +} + +// `setObject:forKey:` just call this with 0 cost. Override this is enough +- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g { + [super setObject:obj forKey:key cost:g]; + if (key && obj) { + // Store weak cache + LOCK(self.weakCacheLock); + [self.weakCache setObject:obj forKey:key]; + UNLOCK(self.weakCacheLock); + } +} + +- (id)objectForKey:(id)key { + id obj = [super objectForKey:key]; + if (key && !obj) { + // Check weak cache + LOCK(self.weakCacheLock); + obj = [self.weakCache objectForKey:key]; + UNLOCK(self.weakCacheLock); + if (obj) { + // Sync cache + NSUInteger cost = 0; + if ([obj isKindOfClass:[UIImage class]]) { + cost = SDCacheCostForImage(obj); + } + [super setObject:obj forKey:key cost:cost]; + } + } + return obj; +} + +- (void)removeObjectForKey:(id)key { + [super removeObjectForKey:key]; + if (key) { + // Remove weak cache + LOCK(self.weakCacheLock); + [self.weakCache removeObjectForKey:key]; + UNLOCK(self.weakCacheLock); + } +} + +- (void)removeAllObjects { + [super removeAllObjects]; + // Manually remove should also remove weak cache + LOCK(self.weakCacheLock); + [self.weakCache removeAllObjects]; + UNLOCK(self.weakCacheLock); +} + +#endif + +@end + @interface SDImageCache () #pragma mark - Properties -@property (strong, nonatomic, nonnull) NSCache *memCache; +@property (strong, nonatomic, nonnull) SDMemoryCache *memCache; @property (strong, nonatomic, nonnull) NSString *diskCachePath; @property (strong, nonatomic, nullable) NSMutableArray *customPaths; @property (strong, nonatomic, nullable) dispatch_queue_t ioQueue; @@ -75,7 +174,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { [_config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCount)) options:0 context:SDImageCacheContext]; // Init the memory cache - _memCache = [[NSCache alloc] init]; + _memCache = [[SDMemoryCache alloc] init]; _memCache.name = fullNamespace; // Init the disk cache @@ -92,11 +191,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { #if SD_UIKIT // Subscribe to app events - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(clearMemory) - name:UIApplicationDidReceiveMemoryWarningNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deleteOldFiles) name:UIApplicationWillTerminateNotification diff --git a/SDWebImage/UIView+WebCacheOperation.m b/SDWebImage/UIView+WebCacheOperation.m index 78d58f41..3d1329cf 100644 --- a/SDWebImage/UIView+WebCacheOperation.m +++ b/SDWebImage/UIView+WebCacheOperation.m @@ -26,7 +26,7 @@ typedef NSMapTable> SDOperationsDictionary; if (operations) { return operations; } - operations = [[NSMapTable alloc] initWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableWeakMemory capacity:0]; + operations = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0]; objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); return operations; } diff --git a/WebImage/Info.plist b/WebImage/Info.plist index 84cef2ba..0295c9d3 100644 --- a/WebImage/Info.plist +++ b/WebImage/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.3.0 + 4.3.1 CFBundleSignature ???? CFBundleVersion - 4.3.0 + 4.3.1 NSPrincipalClass