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协议 ---- NSMapTableNSCache
的淘汰策略 LFU & LRU 两者结合的