`之前在做项目需求(需要缓存几百个头像文件和用户数据)时候用到了一种新的缓存方式,之前是用NSMutableDictionary来缓存,后来了解到了转用NSCache来缓存。但是Dict不会自动删除多余的,会占用很多空间,如下引用所说
我们通常用使用缓存来临时存储短时间使用但创建昂贵的对象。重用这些对象可以优化性能,因为它们的值不需要重新计算。另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃。如果对象被丢弃了,则下次使用时需要重新计算。
NSCache简介
NSCache是苹果官方提供的缓存类,用法与NSMutableDictionary的用法很相似,在AFNetworking和SDWebImage中,使用它来管理缓存。
NSCache在系统内存很低时,会自动释放一些对象(出自苹果官方文档,不过在模拟器中模拟内存警告时,不会做缓存的清理动作) 为了确保接收到内存警告时能够真正释放内存,最好调用一下removeAllObjects方法。
NScache是线程安全的,在多线程操作中,不需要对Cache加锁。
NScache的key只是做强引用,不需要实现NScopying协议。
NSCache和NSMutableDictionary的区别
当一个key-value对在缓存中时,缓存维护它的一个强引用。存储在NSCache中的通用数据类型通常是实现了NSDiscardableContent协议的对象。在缓存中存储这类对象是有好处的,因为当不再需要它时,可以丢弃这些内容,以节省内存。默认情况下,缓存中的NSDiscardableContent对象在其内容被丢弃时,会被移除出缓存,尽管我们可以改变这种缓存策略。如果一个NSDiscardableContent被放进缓存,则在对象被移除时,缓存会调用discardContentIfPossible方法。
NSCache与可变集合NSMutableDictionary有几点不同:
1.NSCache类结合了各种自动删除策略,以确保不会占用过多的系统内存。如果其它应用需要内存时,系统自动执行这些策略。当调用这些策略时,会从缓存中删除一些对象,以最大限度减少内存的占用。
2.NSCache是线程安全的,我们可以在不同的线程中添加、删除和查询缓存中的对象,而不需要锁定缓存区域。
3.不像NSMutableDictionary对象,一个缓存对象不会拷贝key对象。
这些特性对于NSCache类来说是必须的,因为在需要释放内存时,缓存必须异步地在幕后决定去自动修改自身。
NSCache的使用
缓存限制
系统提供了两个属性来设置缓存上限,
默认值为0,表示不限制数量。但需要注意的是,这不是一个严格的限制。如果缓存的数量超过这个数量,缓存中的一个对象可能会被立即丢弃、或者稍后、也可能永远不会,具体依赖于缓存的实现细节。
在后续的测试中,按顺序添加的缓存,会在超出后,按照顺序丢弃删除。
@property NSUInteger countLimit; // limits are imprecise/not strict
默认值是0,表示没有限制。当我们添加一个对象到缓存中时,我们可以为其指定一个消耗(cost),如对象的字节大小。如果添加这个对象到缓存导致缓存总的消耗超过totalCostLimit的值,则缓存会自动丢弃一些对象,直到总消耗低于totalCostLimit值。不过被丢弃的对象的顺序无法保证。
一般不建议这一方式来设定缓存限制。
@property NSUInteger totalCostLimit; // limits are imprecise/not strict
该方法对应的存的方法是- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
这个消耗值用于计算缓存中所有对象的一个消耗总和。当内存受限或者总消耗超过了限定的最大总消耗,则缓存应该开启一个丢弃过程以移除一些对象。不过,这个过程不能保证被丢弃对象的顺序。其结果是,如果我们试图操作这个消耗值来实现一些特殊的行为,则后果可能会损害我们的程序。通常情况下,这个消耗值是对象的字节大小。如果这些信息不是现成的,则我们不应该去计算它,因为这样会使增加使用缓存的成本。如果我们没有可用的值传递,则直接传递0,或者是使用-setObject:forKey:方法,这个方法不需要传入一个消耗值。
存取方法
NSCache提供了若干key-value的存取方法,分别是
- (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;
NSCacheDelegate代理
NSCache还有一个代理协议
@property (nullable, assign) id<NSCacheDelegate> delegate;
协议需要实现的方法是
- (void)cache:(NSCache *)cache willEvictObject:(id)obj;
意在 缓存将要删除对象时调用,不能在此方法中修改缓存。仅仅用于后台的打印,以便于程序员的测试。
???收到内存警告后无法再加入到cache中?
查阅到有资料称:当收到内存警告,调用removeAllObjects 之后,无法再次往缓存池中添加数据.还没找到制造内存警告的方法,所以还没有验证([self ***]方法似乎没有效果.