iOS NSCache 探索

1.NSCache

  • NSCache是苹果官方提供的缓存类,在AFNetworking,SDWebImage等中,使用它来进行图片缓存。
    NSCache头文件
  • name NSCache的名字
  • delegate代理,NSCache中对象将要被删除时调用
  • - (nullable ObjectType)objectForKey:(KeyType)key; 获取NSCache存储的对象
  • //NSCache存储对象的方法
    - (void)setObject:(ObjectType)obj forKey:(KeyType)key;
    - (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;
    //NSCache删除对象的方法
    - (void)removeObjectForKey:(KeyType)key;
    - (void)removeAllObjects;
    
  • totalCostLimit 总消耗限制,超过限制就会对缓存对象进行削减策略
  • countLimit 存储个数限制
  • evictsObjectsWithDiscardedContent使用丢弃的内容逐出对象

2.NSCache使用

NSCacheDemo

如图说是写了这样一个demo来看下NSCache的存储策略是怎样的
image.png

从打印结果中可以看出这里存储了10个对象由于我们设置了countLimit = 5 前面存储的5个被后面存储的5个覆盖了,如果我们推出到后台会出现什么情况呢

Demo退到后台

当我们退出到后台后在日志中我们发现存在NSCache中剩余的5个通过调用removeAllObjects方法也被删除了
Demo发生内存警告

当内存发生警告时也会自动的删除NSCache中存储的对象,但不是全部

总结
NSCache 释放存储对象的条件

  • 设置了 countLimit 并存储对象数超过设置数
  • 设置了 totalCostLimit
  • 手动调用remove方法
  • app进入后台之后
  • 内存警告?清除一部分

NSCahce内部是怎样实现的呢?接下来我们使用GNUstep去探索下

3.NSCache GNUstep源码探索

GNUstep源码NSCache.h

NSCache头文件中定义了一些属性;其中_evictsObjectsWithDiscardedContent标识当前对象是否实现了<NSDiscardableContent>协议,_objects NSCache中存储对象的容器 它是NSMapTable类型的

- (void)setObject:(ObjectType)obj forKey:(KeyType)key我们通过这入口函数来看下内部具体的实现

image.png

- (void)setObject:(ObjectType)obj forKey:(KeyType)key在这个方法中我们看到它调用了- (void) setObject: (id)obj forKey: (id)key cost: (NSUInteger)num这个函数
由于是通过NSMapTable来存储缓存对象那么就可以直接使用key值来直接获取存储的对象

  • 判断了是否已经存储过key这个值,如果存储了把这个值给remove掉
  • [self _evictObjectsToMakeSpaceForObjectWithCost: num];对缓存进行了一个淘汰根据num这个内存消耗
  • newObject进行了赋值和存储
  • 更新了_totalCost的值
_GSCachedObject
  • object:存储的对象
  • key
  • accessCount 通过这个字段来实现LFU(Least Frequently Used 最不经常使用策略)缓存策略
  • cost:消耗
  • isEvictable 是否能被释放
由于代码太长没截完整
  • 计算了需要清除的空间大小
  • 决定哪些对象应该被清除

NSUInteger averageAccesses = ((_totalAccesses / (double)count) * 0.2) + 1;计算平均访问次数 _totalAccesses 内存对象被访问的总数 count缓存对象总数,利用平均访问次数与每个对象的实际访问次数‘obj->accessCount’做对比,通过一个while循环进行逐一驱逐对象;NSEnumerator *e = [_accesses objectEnumerator];通过_accesses获取了e

image.png

_accesses是一个以LRU为策略排序的数组

if (obj->accessCount < averageAccesses && obj->isEvictable) 
[obj->object discardContentIfPossible];
...
spaceNeeded -= cost;

通过这两行代码进行了一个判断:

  • 对象的访问次数小于平均访问次数
  • 当前对象是否为可释放对象
  • 当前对象是否正在被使用
  • 更新可释放空间

总结

  • NSCache使用方便,类似于字典
  • NSCache线程安全
  • NSCache能释放存储对象
  • NSCache的key不会被拷贝,不需要实现Copying协议 ---- NSMapTable
  • NSCache的淘汰策略 LFU & LRU 两者结合的
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容