From 5408da630cb13a43de4557f0f02252d81c7bb973 Mon Sep 17 00:00:00 2001 From: Paul Lavoine Date: Thu, 14 Jun 2018 14:41:56 +0200 Subject: [PATCH 1/6] Enable to change clear cache option --- SDWebImage/SDImageCache.m | 24 ++++++++++++++++++++---- SDWebImage/SDImageCacheConfig.h | 17 +++++++++++++++++ SDWebImage/SDImageCacheConfig.m | 1 + 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 5f87891e..2833e6d7 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -606,7 +606,23 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock { dispatch_async(self.ioQueue, ^{ NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; - NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey]; + + // Compute content date key to be used for tests + NSURLResourceKey cacheContentDateKey = NSURLContentModificationDateKey; + switch (self.config.cacheClearBy) { + case SDImageCacheConfigExpireTypeAccessDate: + cacheContentDateKey = NSURLContentAccessDateKey; + break; + + case SDImageCacheConfigExpireTypeModificationDate: + cacheContentDateKey = NSURLContentModificationDateKey; + break; + + default: + break; + } + + NSArray *resourceKeys = @[NSURLIsDirectoryKey, cacheContentDateKey, NSURLTotalFileAllocatedSizeKey]; // This enumerator prefetches useful properties for our cache files. NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtURL:diskCacheURL @@ -633,12 +649,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { } // Remove files that are older than the expiration date; - NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]; - if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) { + NSDate *modifiedDate = resourceValues[cacheContentDateKey]; + if ([[modifiedDate laterDate:expirationDate] isEqualToDate:expirationDate]) { [urlsToDelete addObject:fileURL]; continue; } - + // Store a reference to this file and account for its total size. NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; currentCacheSize += totalAllocatedSize.unsignedIntegerValue; diff --git a/SDWebImage/SDImageCacheConfig.h b/SDWebImage/SDImageCacheConfig.h index d3cb5421..1ec5e0ca 100644 --- a/SDWebImage/SDImageCacheConfig.h +++ b/SDWebImage/SDImageCacheConfig.h @@ -9,6 +9,17 @@ #import #import "SDWebImageCompat.h" +typedef NS_ENUM(NSUInteger, SDImageCacheConfigExpireType) { + /** + * When the image is accessed it will update this value + */ + SDImageCacheConfigExpireTypeAccessDate, + /** + * The image was obtained from the disk cache (Default) + */ + SDImageCacheConfigExpireTypeModificationDate +}; + @interface SDImageCacheConfig : NSObject /** @@ -49,4 +60,10 @@ */ @property (assign, nonatomic) NSUInteger maxCacheSize; +/** + * The attribute which the clear cache will be checked against when clearing the disk cache + * Default is Modified Date + */ +@property (assign, nonatomic) SDImageCacheConfigExpireType cacheClearBy; + @end diff --git a/SDWebImage/SDImageCacheConfig.m b/SDWebImage/SDImageCacheConfig.m index 923506d0..b146f265 100644 --- a/SDWebImage/SDImageCacheConfig.m +++ b/SDWebImage/SDImageCacheConfig.m @@ -21,6 +21,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week _diskCacheWritingOptions = NSDataWritingAtomic; _maxCacheAge = kDefaultCacheMaxCacheAge; _maxCacheSize = 0; + _cacheClearBy = SDImageCacheConfigExpireTypeModificationDate; } return self; } From 268d6d607aa7ec9ab65fb6df55e1ce4cb3d67493 Mon Sep 17 00:00:00 2001 From: Paul Lavoine Date: Sat, 23 Jun 2018 08:58:06 +0200 Subject: [PATCH 2/6] Refactor cacheClearBy property name --- SDWebImage/SDImageCache.m | 2 +- SDWebImage/SDImageCacheConfig.h | 2 +- SDWebImage/SDImageCacheConfig.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 2833e6d7..627420a5 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -609,7 +609,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { // Compute content date key to be used for tests NSURLResourceKey cacheContentDateKey = NSURLContentModificationDateKey; - switch (self.config.cacheClearBy) { + switch (self.config.diskCacheExpireType) { case SDImageCacheConfigExpireTypeAccessDate: cacheContentDateKey = NSURLContentAccessDateKey; break; diff --git a/SDWebImage/SDImageCacheConfig.h b/SDWebImage/SDImageCacheConfig.h index 1ec5e0ca..22c8f3e4 100644 --- a/SDWebImage/SDImageCacheConfig.h +++ b/SDWebImage/SDImageCacheConfig.h @@ -64,6 +64,6 @@ typedef NS_ENUM(NSUInteger, SDImageCacheConfigExpireType) { * The attribute which the clear cache will be checked against when clearing the disk cache * Default is Modified Date */ -@property (assign, nonatomic) SDImageCacheConfigExpireType cacheClearBy; +@property (assign, nonatomic) SDImageCacheConfigExpireType diskCacheExpireType; @end diff --git a/SDWebImage/SDImageCacheConfig.m b/SDWebImage/SDImageCacheConfig.m index b146f265..3061189e 100644 --- a/SDWebImage/SDImageCacheConfig.m +++ b/SDWebImage/SDImageCacheConfig.m @@ -21,7 +21,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week _diskCacheWritingOptions = NSDataWritingAtomic; _maxCacheAge = kDefaultCacheMaxCacheAge; _maxCacheSize = 0; - _cacheClearBy = SDImageCacheConfigExpireTypeModificationDate; + _diskCacheExpireType = SDImageCacheConfigExpireTypeModificationDate; } return self; } From bfa940352f43e66ae917b3828ff4c14962d3adb3 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 7 Jul 2018 12:29:01 +0800 Subject: [PATCH 3/6] Add option to enable or disable weak memory cache for SDImageCache --- SDWebImage/SDImageCache.m | 28 ++++++++++++++++++++++++++-- SDWebImage/SDImageCacheConfig.h | 14 ++++++++++++-- SDWebImage/SDImageCacheConfig.m | 1 + 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 627420a5..33592b14 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -30,9 +30,13 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { // Private @interface SDMemoryCache () +@property (nonatomic, strong, nonnull) SDImageCacheConfig *config; @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 +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithConfig:(nonnull SDImageCacheConfig *)config; + @end @implementation SDMemoryCache @@ -45,7 +49,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; } -- (instancetype)init { +- (instancetype)initWithConfig:(SDImageCacheConfig *)config { self = [super init]; if (self) { // Use a strong-weak maptable storing the secondary cache. Follow the doc that NSCache does not copy keys @@ -53,6 +57,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { // 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); + self.config = config; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification @@ -69,6 +74,9 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { // `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 (!self.config.shouldUseWeakMemoryCache) { + return; + } if (key && obj) { // Store weak cache LOCK(self.weakCacheLock); @@ -79,6 +87,9 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { - (id)objectForKey:(id)key { id obj = [super objectForKey:key]; + if (!self.config.shouldUseWeakMemoryCache) { + return obj; + } if (key && !obj) { // Check weak cache LOCK(self.weakCacheLock); @@ -98,6 +109,9 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { - (void)removeObjectForKey:(id)key { [super removeObjectForKey:key]; + if (!self.config.shouldUseWeakMemoryCache) { + return; + } if (key) { // Remove weak cache LOCK(self.weakCacheLock); @@ -108,12 +122,22 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { - (void)removeAllObjects { [super removeAllObjects]; + if (!self.config.shouldUseWeakMemoryCache) { + return; + } // Manually remove should also remove weak cache LOCK(self.weakCacheLock); [self.weakCache removeAllObjects]; UNLOCK(self.weakCacheLock); } +#else + +- (instancetype)initWithConfig:(SDImageCacheConfig *)config { + self = [super init]; + return self; +} + #endif @end @@ -163,7 +187,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { _config = [[SDImageCacheConfig alloc] init]; // Init the memory cache - _memCache = [[SDMemoryCache alloc] init]; + _memCache = [[SDMemoryCache alloc] initWithConfig:_config]; _memCache.name = fullNamespace; // Init the disk cache diff --git a/SDWebImage/SDImageCacheConfig.h b/SDWebImage/SDImageCacheConfig.h index 22c8f3e4..b2ef357d 100644 --- a/SDWebImage/SDImageCacheConfig.h +++ b/SDWebImage/SDImageCacheConfig.h @@ -29,15 +29,25 @@ typedef NS_ENUM(NSUInteger, SDImageCacheConfigExpireType) { @property (assign, nonatomic) BOOL shouldDecompressImages; /** - * disable iCloud backup [defaults to YES] + * Whether or not to disable iCloud backup + * Defaults to YES. */ @property (assign, nonatomic) BOOL shouldDisableiCloud; /** - * use memory cache [defaults to YES] + * Whether or not to use memory cache + * @note When the memory cache is disabled, the weak memory cache will also be disabled. + * Defaults to YES. */ @property (assign, nonatomic) BOOL shouldCacheImagesInMemory; +/** + * The option to control weak memory cache for images. When enable, `SDImageCache`'s memory cache will use a weak maptable to store the image at the same time when it stored to memory, and get removed at the same time. + * However when memory warning triggered, since this weak maptable does not hold a strong reference to image instacnce, even when the memory cache itself is purged, some images which are held strongly by UIImageView or other instance can be recoveried again, to avoid re-query from disk cache or network. This may be helpful for case when app enter background and memory purched, cause cell flashing after re-enter foreground. + * Defautls to YES. You can change this option dynamically. + */ +@property (assign, nonatomic) BOOL shouldUseWeakMemoryCache; + /** * The reading options while reading cache from disk. * Defaults to 0. You can set this to `NSDataReadingMappedIfSafe` to improve performance. diff --git a/SDWebImage/SDImageCacheConfig.m b/SDWebImage/SDImageCacheConfig.m index 3061189e..d4f4dd21 100644 --- a/SDWebImage/SDImageCacheConfig.m +++ b/SDWebImage/SDImageCacheConfig.m @@ -17,6 +17,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week _shouldDecompressImages = YES; _shouldDisableiCloud = YES; _shouldCacheImagesInMemory = YES; + _shouldUseWeakMemoryCache = YES; _diskCacheReadingOptions = 0; _diskCacheWritingOptions = NSDataWritingAtomic; _maxCacheAge = kDefaultCacheMaxCacheAge; From 2f665fd71f9028f783fb0f9ccf2754dc1791b9df Mon Sep 17 00:00:00 2001 From: Bogdan Poplauschi Date: Wed, 11 Jul 2018 18:25:33 +0300 Subject: [PATCH 4/6] Added Wu Zhong to the list of collaborators --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6976ecaf..c8bd848b 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ community can help you solve it. - [Bogdan Poplauschi](https://github.com/bpoplauschi) - [Chester Liu](https://github.com/skyline75489) - [DreamPiggy](https://github.com/dreampiggy) +- [Wu Zhong](https://github.com/zhongwuzw) ## Licenses From 233caefec829027b6f073fb5255e9fe66b24f4cb Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 7 Jul 2018 11:48:09 +0800 Subject: [PATCH 5/6] Add cache control for FLAniamtedImage, this allow user to disable memory cache for associated FLAnimatedImage instance --- .../FLAnimatedImageView+WebCache.h | 7 +++++++ .../FLAnimatedImageView+WebCache.m | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h index f046f7a6..680eda3a 100644 --- a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h +++ b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h @@ -52,6 +52,13 @@ */ @property (nonatomic, assign) BOOL sd_predrawingEnabled; +/** + * Cache control for associated FLAnimatedImage object for memory cache. When enabled, we will bind created FLAnimatedImage instance to UIImage, and store it into memory cache to avoid create this instance cause decoding performance. See `UIImage+FLAnimatedImage`. + * When enabled, this may consume more memory, if you are facing memory issue, disable it and let FLAnimatedImage been created just in time and dealloced as it not been used. However, when disabled, this may impact performance since we need query disk cache when current GIF url is cached. + * Defatuls to YES; + */ +@property (nonatomic, assign) BOOL sd_cacheFLAnimatedImage; + /** * 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 644591b6..8af7a0eb 100644 --- a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m +++ b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m @@ -56,6 +56,19 @@ objc_setAssociatedObject(self, @selector(sd_predrawingEnabled), @(sd_predrawingEnabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } +- (BOOL)sd_cacheFLAnimatedImage { + BOOL cacheFLAnimatedImage = YES; + NSNumber *value = objc_getAssociatedObject(self, @selector(sd_cacheFLAnimatedImage)); + if ([value isKindOfClass:[NSNumber class]]) { + cacheFLAnimatedImage = value.boolValue; + } + return cacheFLAnimatedImage; +} + +- (void)setSd_cacheFLAnimatedImage:(BOOL)sd_cacheFLAnimatedImage { + objc_setAssociatedObject(self, @selector(sd_cacheFLAnimatedImage), @(sd_cacheFLAnimatedImage), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + - (void)sd_setImageWithURL:(nullable NSURL *)url { [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; } @@ -113,12 +126,14 @@ 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]; + animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData optimalFrameCacheSize:weakSelf.sd_optimalFrameCacheSize predrawingEnabled:weakSelf.sd_predrawingEnabled]; } else { animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData]; } dispatch_async(dispatch_get_main_queue(), ^{ - image.sd_FLAnimatedImage = animatedImage; + if (weakSelf.sd_cacheFLAnimatedImage) { + image.sd_FLAnimatedImage = animatedImage; + } weakSelf.animatedImage = animatedImage; weakSelf.image = nil; if (group) { From 98c28a4419aba39d50d9c99088bbb6dd0f85b38b Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Thu, 12 Jul 2018 01:47:13 +0800 Subject: [PATCH 6/6] Update the comments and fix typo --- SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h | 2 +- SDWebImage/SDImageCacheConfig.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h index 680eda3a..1a0d55b2 100644 --- a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h +++ b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.h @@ -54,7 +54,7 @@ /** * Cache control for associated FLAnimatedImage object for memory cache. When enabled, we will bind created FLAnimatedImage instance to UIImage, and store it into memory cache to avoid create this instance cause decoding performance. See `UIImage+FLAnimatedImage`. - * When enabled, this may consume more memory, if you are facing memory issue, disable it and let FLAnimatedImage been created just in time and dealloced as it not been used. However, when disabled, this may impact performance since we need query disk cache when current GIF url is cached. + * When enabled, this may consume more memory, if you are facing memory issue, disable it and let FLAnimatedImage been created just in time and dealloced as it not been used. However, when disabled, this may impact performance since we need query disk cache, create FLAnimatedImage and decoding even when the current GIF url is cached. * Defatuls to YES; */ @property (nonatomic, assign) BOOL sd_cacheFLAnimatedImage; diff --git a/SDWebImage/SDImageCacheConfig.h b/SDWebImage/SDImageCacheConfig.h index b2ef357d..c0de6888 100644 --- a/SDWebImage/SDImageCacheConfig.h +++ b/SDWebImage/SDImageCacheConfig.h @@ -43,7 +43,7 @@ typedef NS_ENUM(NSUInteger, SDImageCacheConfigExpireType) { /** * The option to control weak memory cache for images. When enable, `SDImageCache`'s memory cache will use a weak maptable to store the image at the same time when it stored to memory, and get removed at the same time. - * However when memory warning triggered, since this weak maptable does not hold a strong reference to image instacnce, even when the memory cache itself is purged, some images which are held strongly by UIImageView or other instance can be recoveried again, to avoid re-query from disk cache or network. This may be helpful for case when app enter background and memory purched, cause cell flashing after re-enter foreground. + * However when memory warning is triggered, since the weak maptable does not hold a strong reference to image instacnce, even when the memory cache itself is purged, some images which are held strongly by UIImageViews or other live instances can be recovered again, to avoid later re-query from disk cache or network. This may be helpful for the case, for example, when app enter background and memory is purged, cause cell flashing after re-enter foreground. * Defautls to YES. You can change this option dynamically. */ @property (assign, nonatomic) BOOL shouldUseWeakMemoryCache;