阅读PINCache源码
最后更新于
最后更新于
前言: 这里我只介绍一下最核心的地方,至于很多细节并不提及,有兴趣的小伙伴可以查看源码
PINCache 是一个 iOS 的三方开源库,提供了缓存的功能。 下面我们了解一下它的实现原理。 学习之前我们可以先了解 5 个东西
项目结构
内存缓存
磁盘缓存
线程安全
清理机制
下图展示了 PINCache 架构
PINCache
提供的方法内部都是调用的 MemoryCache
或者 DiskCache
,其实可以理解为 PINCache类
封装了 MemoryCache
和 DiskCache
两个类
MemoryCache
和 DiskCache
也各自提供了自己的 Interface
,如果开发者愿意的话 也可以根据自己情况单独调用 MemoryCache
和 DiskCache
内存缓存是其实就是持有一块内存不释放,然后对其添加要存储的对象,和清理要删除的对象。
优点:
读取数据速度快 (因为内存缓存是内存对象)
缺点:
kill app 就被释放了,无法本地持久化存储。
空间小,不能什么都放在内存,内存是寸土寸金的地方。
从上面的截图可以看出来,PINCache
内存缓存核心用的是 NSDictionary
对象来进行存储和读取的。
磁盘缓存是持久化存储,在 iOS 中一般就是存在沙盒中。
优点:
可以持久化存储,App 被 Kill 也无妨。
空间大
缺点:
访问大数据的速度要慢与内存缓存。
下面是 PINCache
的磁盘缓存的实现逻辑,可以看出来,PINCache 磁盘缓存用了 NSKeyArchiver
和 NSKeyUnarchiver
来实现的对要存储对象的磁盘缓存。
它的存储步骤大致如下(读取相反):
用 Key+ 固定 URL 拼接成唯一的 FileURL
在把要存的对象 object 通过 NSKeyArchiver 序列化成 data
最后将 data 存入 fileURL 的沙盒路径下
因为涉及线程安全有很多内容,这里只介绍一下 PINCache
的线程安全是如何处理的。
当程序中开启多线程时,并发的用 写
操作同一个变量的时候,会出现线程不安全,例如 同时 2 个线程同时对变量 A 进行 写
操作,虽然程序里的执行顺序是操作 A 早于操作 B,但是因为是多线程并发,实际执行的时候却是操作 B 早于操作 A,这样你的逻辑就出错了,本来你想的是用操作 B 的值覆盖掉操作 A,现在反过来了,甚至可能会 Crash。
解决这个问题有很多种方法,这里只看下 PINCache 的实现,PINCache
保障线程安全使用的是 条件锁
和 信号量
, 下面是它的实现代码
不懂条件锁和信号量的童鞋可以单独查一下使用和区别。
这里说明一下: 正常情况,我们只需要保证写入就可以了,但是我们看到PINCache读取也使用了Lock,我们大致猜测,可能预防一些比较大的文件,正在写入或者写入一半的时候,这个时候去读取,会导致读取不完整的情况
清理机制这里我就简单带过了 下面介绍几个 PINCache
里面的属性
Date: 设置一个过期时间,当缓存内容超过这个日期,会被自动清理掉。
Cost: 成本代价,未每个存储的 Value 设置一个 Cost,当总的 Cost>LimitCost 的时候会去清理缓存内容来平衡。
ByteCount: 磁盘缓存的总字节大小,每次缓存的时候都会重新计算更新一次。
剩下一些机制,这里就不再介绍了,大家有兴趣看源码就好了。
我们通过阅读 PINCache
的源码了解到核心的实现原理,如果你之前对缓存框架一头雾水,现在是不是清楚一些了呢。
在 PINCache
中,内存缓存使用的是 NSMutableDictionary
内存缓存: 信号量
磁盘缓存: 条件锁