第一次见到NSCache,是在SDWebImage中。SDWebImage的内存缓存机制就是通过NSCache完成的。所以可能你不太了解这个类,但是其实一直在使用它。

为什么要使用NSCache?

我们通常用缓存来临时存储短时间使用但创建昂贵的对象。重用这些对象可以优化性能,因为它们的值不需要重新计算。另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃。如果对象被丢弃了,则下次使用时需要重新计算。

NSCache是一个可变的集合,主要用来存储key-value对。它有着和NSMutableDictionary类似的API。实际上,NSCache就像是一个会自动移除对象来释放内存的NSMutableDictionary

( i d o o o o ) i i i i o d d d d b ) ) ) ) j s s r r e e e e e c t t m m t O O o o F b b v v o j j e e r e e O A K c c b l e t t j l y : : e O : ( ( c b ( i i t j i d d F e d ) ) o c ) o o r t k b b K s e j j e y y f f : o o ( r r i K K d e e ) y y k : : e ( ( y i i d d ) ) k k e e y y c o s t : ( N S U I n t e g e r ) n u m

NSCahce与可变集合不同之处:

1.NSCache类有自己的自动删除策略,以确保缓存不用使用太多的系统内存。如果其他应用需要内存,则自动删除策略会从缓存中删除一些项目,从而最大限度的减少内存占用。 2.NSCache是线程安全的,我们可以从不同的线程中添加、删除和查询缓存中的对象,而不需要锁定缓存区域。其中线程安全是pthread_mutex完成的。 3.明显区别于NSMutableDictionary的是,键对象不会被retain。(键不需要实现NSCopying协议)

需要注意的点

NSCache提供了一个delegate方法:

o i d ) c a c h e : ( N S C a c h e ) c a c h e w i l l E v i c t O b j e c t : ( i d ) o b j

需要注意的是在这个代理方法中不能修改NSCache对象,也就是只能测试使用。

NSCache提供了一系列的属性来限制缓存的大小。比如属性countLimit限定了缓存最多维护的对象的个数,属性totalCostLimit限定了缓存能维持的最大内存。 在存取方法中,有一个setObject:forKey:cost: 方法,它和setObject:forKey: 方法类似,但是带着cost参数。cost表示每次的消耗,如对象占用的字节数。当内存不足或者所有缓存对象的总消耗超过了最大值时,缓存会移除其中的一些对象。

上面这些内容,看起来很美好,但是问题来了。从缓存中移除对象并不能保证顺序,也就是有可能刚写入的对象,就被丢弃了。如果你要使用cost来完成一些特殊的需求,那你可能要失望了。所以计算cost值,可能反而会增加使用缓存的代价。

另外countLimittotalCostLimit属性都是不精确或者不严格的,也就是如果缓存的数量超过了countLimit,或者缓存维持的最大内存超过了totalCostLimit,缓存中的对象可能会被丢弃,也可能不会,具体的策略只有苹果自己知道。 虽然苹果提供了NSDiscardableContent来控制对象是否会被自动移除的机制,但是这可能会让你碰到更多的问题。

总结

虽然NSCache有一些缺点,但是也应该去积极使用。如果我们需要构建内存缓存机制,就应该选用NSCache而非NSDictionary,这样可以减少我们应用对内存的占用,从而达到优化内存的目标。