iOS 缓存

作者: yue_kyy | 来源:发表于2019-01-28 15:24 被阅读0次

    一、为什么使用缓存

    缓存的目的是以空间换时间。

    出于优化考虑:服务器压力、用户体验、用户流量等;

    出于功能考虑:离线存储、微信会话列表、新闻列表等;

    重度使用缓存的 APP:微信、微博等。

    二、iOS 上的缓存框架

    NSCache、PINCache、YYCache、SDWebImage(分析 SDImageCache 部分)

    1、NSCache

    苹果提供的一个简单的内存缓存;

    类似 NSDictionary 一个可变的集合;

    提供了可设置缓存大小与内存大小限制的方式;

    保证了处理的数据的线程安全性;

    内存警告时自动清理部分缓存数据

    NSCache API

    2、PINCache

    PINCache 项目是在 Tumblr 宣布不再维护 TMCache 后,由 Pinterest 维护和改进的基于 TMCache 的一个内存缓存,修复了 TMCache 存在的性能和死锁问题。

    3、YYCache

    YYCache 是国内开发者 ibireme 开源的一个线程安全的高性能缓存组件。

    4、SDWebImage

    SDWebImage 框架通过给 UIImageView 和 UIButton 添加分类,实现了一个异步下载图片并且支持缓存的功能。整个框架的接口简洁,分工明确。

    三、线程安全

    这些缓存框架都是线程安全的。

    多线程操作共享数据不会出现意想不到的结果就是线程安全的,否则则是不安全的。多个线程同时访问或读取同一共享数据,每个线程的读到的数据是一样的,则不存在线程不安全。如果多个线程对同一资源进行读写操作,那么每个线程读到的结果不可预料,线程是不安全的。

    接下来:

    1、iOS 中有哪些方法保证线程安全;

    2、这些缓存框架是如何保证线程安全的。

    四、iOS 开发中的锁

    1、OSSPinLock

    2、dispatch_semaphore

    3、pthread_mutex  pthread_mutex(recursive)  NSRecursiveLock

    4、NSLock

    5、NSCondition  NSConditionLock

    6、@synchronized

    理解iOS开发中的锁

    锁的性能

    YYCache 和 PINCache 在内存缓存使用的是 pthread_mutex,在磁盘缓存使用的是Semaphone。 SDWebImage 在内存缓存使用的是 NSCache,本身就是线程安全的。在磁盘缓存使用串行队列来保证线程安全。

    保证线程安全的方式

    五、缓存

    1、缓存的读取

    缓存读取的逻辑大致为:

    先访问内存缓存,再访问磁盘缓存(写入、读取、查询、删除);

    读取缓存时,如果在内存缓存中无法获取对应的缓存,则会去磁盘缓存中寻找。如果在磁盘缓存中找到了对应的缓存,则会将该对象再次写入内存缓存中,保证下一次尝试获取同一缓存时能够在内存中就能返回,提高速度。

    2、二级缓存

    MemoryCache

    PINMemoryCache 通过维护一个 dic 记录 object 最后一次访问的时间,通过排序来实现 LRU;

    YYMemoryCache 缓存内部通过双向链表和 NSDictionary 实现 LRU 淘汰算法;

    SDImageCache 缓存使用的是 NSCache。

    DiskCache

    PINDiskCache 和 SDImageCache 是基于文件系统的;

    YYDiskCache 采用了 SQLite&文件系统实现。

    3、SDWebImage

    SDWebImage流程图

    SDImageCache 默认图片清理时间为一周。

    SDImageCache 缓存的写入:

    1、将图片缓存在内存中;

    2、判断图片格式是 png 或 jpeg,将图片转化为 NSData 数据;

    3、如果是在 mac_os 系统中,直接将图片转化为 NSBitmapImageRep 数据;

    4、获取图片的存储路径,其中图片的文件名通过传入的 key 经过 md5 加密后获得的;

    5、将图片存储在磁盘中。

    SDImageCache 缓存的删除:

    1、获取磁盘中图片的最后修改日期;

    2、根据日期将图片进行分类,将超过最长存放时间的文件存储在删除数组中,其他的文件信息存储在另一个 dic 中,并计算除去要删除的文件之外的文件大小;

    3、根据删除数组中的文件路径,将对应的文件删除;

    4、判断剩下的文件大小是否超过用户现在的最大容量;

    5、如果超过,则将剩余文件按修改时间进行升序排列,删除修改时间最早的文件,直到剩余文件大小小于最大磁盘容量。

    清理时机:

    系统内存不足时,会将内存中所有的图片缓存删除;

    当系统进入后台时,会对磁盘中的文件数据进行清理;

    当收到程序关闭通知时,会对磁盘中的文件数据进行清理。

    4、PINCache

    PINCache 是线程安全的键值对缓存框架,用于缓存一些临时数据或需要频繁加载的数据。

    类图

    PINCache 除了可以按键取值、按键存值、按键删除值之外,还可以移除某个日期之外的缓存数据、删除所有缓存、限制缓存大小。

    PINCacheAPI

    PINMemoryCache:

    维护了三个 dic,分别为_dictionary、_dates、_costs,字典的 key 相同,value 分别为对象、最后访问日期、大小。

    清理缓存:

    内存警告和进入后台时,默认自动清除所有的内存缓存。

    PINDiskCache

    PINDiskCache 以文件形式存储缓存,在内存中维护了两个  dic分别为 _dates、_sizes,分别存储了文件的最后编辑时间和文件大小。

    在初始化时子线程遍历硬盘缓存初始化这两个值,开发中可以根据业务逻辑调用api删除硬盘缓存。

    支持清理的维度:age、byte。

    5、YYCache

    YYCache:提供了最外层的接口,调用了 YYmemoryCache 和 YYDiskCache 的相关方法;

    YYMemoryCache:负责处理容量小,相对高速的内存缓存。线程安全,支持手动和自动清理缓存等功能;

    _YYLinkedMap:YYMemoryCache 使用的双向链表类;

    _YYLinkedMapNode:是 _YYLinkedMap 使用的节点类;

    YYDiskCache:负责处理容量大,相对低速的磁盘缓存。线程安全,支持异步操作,自动和手动清理缓存等功能;

    YYKVStorage:YYDiskCache 的底层实现类,用于管理磁盘缓存;

    YYKVStorageItem:内置在 YYKVStorage 中,是 YYKVStorage 内部用于封装某个缓存的类。

    API

    YYMemoryCache:

    将需要缓存的对象与传入的 key 关联起来,类似于 NSCache。

    不同于 NSCache 的是,它的内部有:

    缓存淘汰算法:LRU 算法来淘汰使用频率较低的缓存;

    缓存清理策略:三个维度分别为 count(缓存数量)、cost(开销)、age(距上一次的访问时间)。可根据不同的需求清理某一维度超标的缓存。

    无论从哪一维度清理缓存,都是从使用频率最低的那个缓存开始清理。

    在 YYMemoryCache 中,使用了双向链表来保存这些缓存:

    当写入一个新的缓存时,要把这个缓存节点放到链表头部,并且原链表头部的缓存节点要变成现在链表的第二个节点;

    当访问一个已有的缓存时,要把这个缓存节点移动到链表的头部,原位置两侧的缓存接上,原头部节点变为第二个;

    (根据清理维度)自动清理缓存时,要从链表的最后端逐个清理。

    清理缓存:

    内存警告和进入后台时,默认自动清除所有的内存缓存。

    YYDiskCache:

    与第一级缓存相同点是:

    都具有查询、写入、读取、删除缓存的接口;

    不直接操作缓存,通过另一个类(YYKVStorage)来操作;

    使用 LRU 算法来清理缓存;

    支持 cost、count、age 三个维度清理不符合标准的缓存。

    不同点是:

    1、根据缓存数据的大小来采取不同的形式的缓存:

    数据库 sqlite:针对小容量缓存,缓存的 data 和元数据都保存在数据库里;

    文件+数据库形式:针对大容量缓存,缓存的 data 写在文件系统中,其元数据保存在数据库中。

    2、除了 cost、count、age 三个维度,还添加了一个磁盘容量的维度。

    六、缓存框架的选型

    由图可见:

    1、YYMemoryCache 的性能不错,仅次于 NSDictory+OSSpinLock;

    2、NSCache 的写入性能较差。读写性能不错;

    3、PINMemoryCache 的读写性能还可以,但读取速度差于 NSCache;

    由图可见:

    1、存取小数据时(NSNumber),YYDiskCache的性能远远高于基于文件存储的库;

    2、较大数据的存取性能比较接近,但得益于 SQLite 存储的元数据,YYDiskCache 实现了 LRU 淘汰算法、更快的数据统计,更多的容量控制选项。

    总结

    1、选择合适的线程锁;

    2、选择合适的数据结构;

    3、选择合适的线程来操作不同的任务;

    4、选择合适的存储方式;

    5、选择底层的类;

    6、变量、方法的命名以及接口的设计。

    ppt:https://github.com/yuetianlu/cache_ppt

    参考:

    https://blog.ibireme.com/2015/10/26/yycache/

    https://juejin.im/post/5a657a946fb9a01cb64ee761

    https://juejin.im/post/5a4080d16fb9a0451969d0aa

    https://www.cnblogs.com/fengmin/p/5318782.html

    https://bestswifter.com/ios-lock/

    http://www.cocoachina.com/ios/20171218/21570.html

    https://blog.csdn.net/u012834750/article/details/69398216

    相关文章

      网友评论

        本文标题:iOS 缓存

        本文链接:https://www.haomeiwen.com/subject/hsuajqtx.html