源码解析 — YYCache

11307963-367208362d4f840d

封面.jpg

前言:准备看下YY系列中的YYWebImage框架,发现该框架是使用YYCache来做缓存的。那就从缓存开始吧.
先奉上YYCache框架的地址以及作者的设计思路
学习YYCache框架你可以get到:
1.优雅的代码风格
2.优秀的接口设计
3.YYCache的层次结构
4.YYMemoryCache类的层次结构和缓存机制
5.YYDiskCache类的层次结构和缓存机制

YYCache

12307963-a3d92756bf3d36bc

YYCache结构.png

YYCache最为食物链的最顶端的男人,并没有什么好说的,所以我们就从YYMemoryCacheYYDiskCache开始吧。

YYMemoryCache

YYMemoryCache内存储存是的原理是利用CFDictionary对象的 key-value开辟内存储存机制和双向链表原理来实现LRU算法。这里是官方文档对CFDictionary的解释:

13307963-9600e9680440fdfd

YYMemoryCache类结构图.png

YYMemoryCache初始化的时候会建立空的私有对象YYLinkedMap链表,接下来所有的操作其实就是对这个链表的操作。当然,YYMemoryCache提供了一个定时器接口给你,你可以通过设置autoTrimInterval属性去完成每隔一定时间去检查countLimitcostLimit是否达到了最大限制,并做相应的操作。

YYMemoryCache以block的形式给你提供了下面接口:

  • didReceiveMemoryWarningBlock(当app接受到内存警告)
  • didEnterBackgroundBlock (当app进入到后台)

当然,你也可以通过设置相应的shouldRemoveAllObjectsOnMemoryWarningshouldRemoveAllObjectsWhenEnteringBackground值来移除YYMemoryCache持有的链表。

下面我们来看看YYMemoryCache类的增,删,查等操作。在这之前我们先看看YYLinkedMap这个类。

1.YYLinkedMap内部结构

YYLinkedMap作为双向链表,主要的工作是为YYMemoryCache类提供对YYLinkedMapNode节点的操作。下图绿色部分代表节点:

14307963-b7d65e7786919a4f

双向链表结构.png

下图是链表节点的结构图:

15307963-8b863f1d8ad804e7

链表节点.png

现在我们先来看如何去构造一个链表添加节点:

16307963-2e0a14f8e758e50e

setObject.png

你可以点击这里自己去操作双向链表

17307963-1e6f08083f16c341

addNode.gif

链表移除节点的操作:

18307963-30e04c1e0a8c53a1

removeNode.gif

YYMemoryCache类还为我们提供了下列接口方便我们调用:

总结:YYMemoryCache是利用key-value机制内存缓存类,所有的方法都是线程安全的。如果你熟悉NSCache类,你会发现两者的接口很是相似。
当然YYMemoryCache有着自己的特点:
1.YYMemoryCache采用LRU(least-recently-used)算法来移除节点。
2.YYMemoryCache可以用countLimitcostLimitageLimit属性做相应的控制。
3.YYMemoryCache类可以设置相应的属性来控制退到后台或者接受到内存警告的时候移除链表。

YYKVStorage

YYKVStorage是一个基于sql数据库和文件写入的缓存类,注意它并不是线程安全。你可以自己定义YYKVStorageType来确定是那种写入方式:

1.写入和更新

我们看看Demo中直接用YYKVStorage储存NSNumber和NSData YYKVStorageTypeFileYYKVStorageTypeSQLite类型所用的时间:

19307963-f7193b38dd0e8df6

7.png

你可以发现在储存小型数据NSNumberYYKVStorageTypeFile类型是YYKVStorageTypeSQLite大约4倍多,而在大型数据的时候两者的表现是相反的。显然选择合适的储存方式是很有必要的。
这里需要提醒的事:

  • DemoYYKVStorageTypeFile类型其实不仅写入了本地文件也同时写入了数据库,只不过数据库里面存的是除了value值以外的key, filename, size, inline_data(NULL), modification_time , last_access_time, extended_data字段。

插入或者是更新数据库

2.读取

我们尝试的去缓存里面拿取数据,我们发现当为YYKVStorage对象type不同,存取的方式不同所以读取的方式也不同:
1.因为在插入的时候我们就说了,当为YYKVStorageTypeFile类型的时候数据是存在本地文件的其他存在数据库。所以YYKVStorage对象先根据key从数据库拿到数据然后包装成YYKVStorageItem对象,然后再根据filename读取本地文件数据赋给YYKVStorageItem对象的value属性。
2.当为YYKVStorageTypeSQLite类型就是直接从数据库把所有数据都读出来赋给YYKVStorageItem对象。

3.删除

YYKVStorage的type当为YYKVStorageTypeFile类型是根据key将本地和数据库都删掉,而YYKVStorageTypeSQLite是根据key删除掉数据库就好了。

我们这里分别列取了增删改查的单个key的操作,你还可以去批量的去操作key的数组。但是其实都大同小异的流程,就不一一累述了。上个图吧:

20307963-a9967cf4a949dfcb
这个类也就看的差不多了,但是要注意的事,YYCache作者并不希望我们直接使用这个类,而是使用更高层的YYDiskCache类。那我们就继续往下面看吧。

YYDiskCache

YYDiskCache类有两种初始化方式:

YYDiskCache类持有一个YYKVStorage对象,但是你不能手动的去控制YYKVStorage对象的YYKVStorageTypeYYDiskCache类初始化提供一个threshold的参数,默认的为20KB。然后根据这个值得大小来确定YYKVStorageType的类型。

因为YYDiskCache类的操作其实就是去操作持有的YYKVStorage对象,所以下面的部分会比较建简略。

写入和更新

在调用YYKVStorage对象的储存操作前主要做了下面几项操作:
1.key和object的判空容错机制
2.利用runtime机制去取extendedData数据
3.根据是否定义了_customArchiveBlock来判断选择序列化object还是block回调得到value
4.value的判空容错机制
5.根据YYKVStorage的type判断以及_inlineThreshold和value值得长度来判断是否选择以文件的形式储存value值。上面我们说过当value比较大的时候文件储存速度比较快速。
6.如果_customFileNameBlock为空,则根据key通过md5加密得到转化后的filename.不然直接拿到_customFileNameBlock关联的filename。生成以后操作文件的路径
做完上面的操作则直接调用YYKVStorage储存方法,下面是实现代码:

读取

读取操作一般都是和写入操作相辅相成的,我们来看看在调用YYKVStorage对象的读取操作后做了哪些操作:
1.item.value的判空容错机制
2.根据_customUnarchiveBlock值来判断是直接将item.value block回调还是反序列化成object
3.根据object && item.extendedData 来决定是否runtime添加extended_data_key属性

删除

删除操作就是直接调用的YYKVStorage对象来操作了。

当然,YYDiskCacheYYMemoryCache一样也给你提供了一些类似limit的接口供你操作。

YYKVStorage不一样的是,作为更高层的YYDiskCache是一个线程安全的类。你应该使用YYDiskCache而不是YYKVStorage

最后再带一笔食物端最顶端的男人YYCache,当他写入的时候会同时调用YYDiskCache磁盘操作和YYMemoryCache内存操作。读取的时候先从内存读取,因为在内存的读取速度比磁盘快很多,如果没有读取到数据才会去磁盘读取。

读后感只有四个字:

如沐春风

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部