第三方库SDWebImage①缓存-①NSCache

必备知识架构-第三方库SDWebImage①缓存-①NSCache

[toc]

一、NSCache的认识

1、为什么内存缓存要基于 NSCache?

NSCache和NSMutableDictionary,它们是iOS中常用的两个缓存类,基本上相同,都是健-值形式的内存缓存方式

1、NSCache的使用很方便,提供了类似可变字典的使用方式,但它比可变字典更适用于实现缓存,最重要的原因为NSCache是线程安全的,使用NSMutableDictionary自定义实现缓存时需要考虑加锁和释放锁NSCache已经帮我们做好了这一步,即在开发者自己不编写加锁代码的前提下,多个线程便可以同时访问NSCache。

2、其次,在内存不足时NSCache会自动释放存储的对象,不需要手动干预,如果是自定义实现需要监听内存状态然后做进一步的删除对象的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@interface NSCache <KeyType, ObjectType> : NSObject {
@private
id _delegate;
void *_private[5];
void *_reserved;
}

@property (copy) NSString *name;

@property (nullable, assign) id<NSCacheDelegate> delegate;

- (nullable ObjectType)objectForKey:(KeyType)key;
- (void)setObject:(ObjectType)obj forKey:(KeyType)key; // 0 cost
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
- (void)removeObjectForKey:(KeyType)key;

- (void)removeAllObjects;

// NSCache可以指定缓存的限额,当缓存超出限额自动释放内存
// ①对象缓存可持有最大的数量 ,默认是0 没有限制),一旦超出限额,会自动删除之前添加的缓存数据
@property NSUInteger countLimit; // limits are imprecise/not strict
// ②缓存中可持有的最大空间 默认是0(没有限制)
@property NSUInteger totalCostLimit; // limits are imprecise/not strict

// 管理丢弃内容
// 是否可以自动缓存清除可丢弃的内容,默认是YES
@property BOOL evictsObjectsWithDiscardedContent;

@end

3、还有一点就是NSCachekey不需要实现NSCopying协议,因为NSCache的键key不会被复制/拷贝。在键key不支持拷贝操作的情况下,该类用起来比字典更方便。

2、NSCache什么时候会删除缓存中的对象

NSCache删除缓存中的对象会在以下情形中发生:

  • NSCache缓存对象自身被释放
  • 手动调用removeObjectForKey:removeAllObjects方法
  • 缓存中对象的个数大于countLimit,或缓存中对象的总cost值大于totalCostLimit
  • 程序进入后台后
  • 收到系统的内存警告

二、SDMemoryCache的认识

以上已说明内存缓存要基于 NSCache,所以SDMemoryCache要继承于NSCache,源码如下:

1
2
3
4
5
6
7
8
9
10
// SDMemoryCache.h

/**
A memory cache which auto purge the cache on memory warning and support weak cache.
*/
@interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType> <SDMemoryCache> // 请注意这里除继承 NSCache <KeyType, ObjectType> 外,还有一个 与 SDMemoryCache 类名同名的 <SDMemoryCache> 协议

@property (nonatomic, strong, nonnull, readonly) SDImageCacheConfig *config;

@end

可以看出类@interface SDMemoryCache还要遵守<SDMemoryCache>协议。虽然类@interface SDMemoryCache没提供什么方法,但与 SDMemoryCache 类名同名的<SDMemoryCache>协议提供了。

<SDMemoryCache>协议源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
A protocol to allow custom memory cache used in SDImageCache.
*/
@protocol SDMemoryCache <NSObject>

@required

- (nonnull instancetype)initWithConfig:(nonnull SDImageCacheConfig *)config;

- (nullable id)objectForKey:(nonnull id)key;

- (void)setObject:(nullable id)object forKey:(nonnull id)key;

- (void)setObject:(nullable id)object forKey:(nonnull id)key cost:(NSUInteger)cost;

- (void)removeObjectForKey:(nonnull id)key;
- (void)removeAllObjects;

@end

1、为什么另外建了个与类名SDMemoryCache同名的<SDMemoryCache>协议,并把方法提到了<SDMemoryCache>协议中?

答:为了当你想要要使用自定义的缓存类的时候,可以不用继承@interface SDMemoryCache,而只需遵循<SDMemoryCache>协议的方便。

设计模式-①概览.md

// 开闭原则:对扩展开放,对修改封闭.
// 里氏替换原则:应用程序中任何父类对象出现的地方,我们都可以用其子类的对象来替换,并且可以保证原有程序的逻辑行为和正确性。因为这里父类是抽象类,所以肯定遵守里氏替换原则。

2、内存缓存SDMemoryCache、磁盘缓存SDDiskCache的基类各是什么?

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 内存类
@interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType> <SDMemoryCache>

@end


// 磁盘缓存类
@interface SDDiskCache : NSObject <SDDiskCache>

@property (nonatomic, strong, readonly, nonnull) SDImageCacheConfig *config;

- (void)moveCacheDirectoryFromPath:(nonnull NSString *)srcPath toPath:(nonnull NSString *)dstPath;

@end

三、内存缓存的设计

1、SDImageCache

id<SDMemoryCache> memoryCache;id<SDDiskCache> diskCache;

一个SDImageCache对象,由内存和磁盘共同控制缓存。控制的策略由SDImageCacheConfig类来定制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@interface SDImageCache : NSObject

#pragma mark - Properties
@property (nonatomic, copy, nonnull, readonly) SDImageCacheConfig *config;

@property (nonatomic, strong, readonly, nonnull) id<SDMemoryCache> memoryCache;
@property (nonatomic, strong, readonly, nonnull) id<SDDiskCache> diskCache;

@property (nonatomic, copy, nonnull, readonly) NSString *diskCachePath;

@property (nonatomic, copy, nullable) SDImageCacheAdditionalCachePathBlock additionalCachePathBlock;

#pragma mark - Singleton and initialization

/**
* Returns global shared cache instance
*/
@property (nonatomic, class, readonly, nonnull) SDImageCache *sharedImageCache;

/**
* Init a new cache store with a specific namespace
*
* @param ns The namespace to use for this cache store
*/
- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns;

同时SDImageCache还有个类目

1
2
3
4
5
6
7
8
9
// SDImageCache.h
@interface SDImageCache (SDImageCache) <SDImageCache>

@end

// SDImageCacheDefine.h
@protocol SDImageCache <NSObject>

@end

内存和磁盘共同控制缓存策略定制类SDImageCacheConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// SDImageCacheConfig.h
@interface SDImageCacheConfig : NSObject <NSCopying>

// 是否使用内存做缓存,默认为YES
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;

@property (assign, nonatomic) BOOL shouldRemoveExpiredDataWhenEnterBackground;

// 缓存图片的最长时间,单位是秒,默认是缓存一周
@property (assign, nonatomic) NSTimeInterval maxDiskAge;
// 缓存占用最大的空间,单位是字节
@property (assign, nonatomic) NSUInteger maxDiskSize;


@property (assign, nonatomic) NSUInteger maxMemoryCost;
@property (assign, nonatomic) NSUInteger maxMemoryCount;

/*
* The attribute which the clear cache will be checked against when clearing the disk cache
* Default is Modified Date
*/
@property (assign, nonatomic) SDImageCacheConfigExpireType diskCacheExpireType;



@end

2、SDImageCachesManager

管理NSArray<id> *caches;

一张图片就是一份SDImageCache元素对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
A caches manager to manage multiple caches.
*/
@interface SDImageCachesManager : NSObject <SDImageCache>

@property (nonatomic, class, readonly, nonnull) SDImageCachesManager *sharedManager;

@property (nonatomic, assign) SDImageCachesManagerOperationPolicy queryOperationPolicy;
@property (nonatomic, assign) SDImageCachesManagerOperationPolicy storeOperationPolicy;
@property (nonatomic, assign) SDImageCachesManagerOperationPolicy removeOperationPolicy;
@property (nonatomic, assign) SDImageCachesManagerOperationPolicy containsOperationPolicy;
@property (nonatomic, assign) SDImageCachesManagerOperationPolicy clearOperationPolicy;

@property (nonatomic, copy, nullable) NSArray<id<SDImageCache>> *caches; // 一张图片就是一份SDImageCache元素对象。

- (void)addCache:(nonnull id<SDImageCache>)cache;
- (void)removeCache:(nonnull id<SDImageCache>)cache;

@end

###

END