disk cache(磁盘缓存) 和 memory cache(内存缓存)
内存缓存
1.快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取
2.时效性:一旦该进程关闭,则该进程的内存则会清空。
磁盘缓存
硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。
例如:在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)
三级缓存原理:
->先去内存看,如果有,直接加载
->如果内存没有,择取硬盘获取,如果有直接加载
->如果硬盘也没有,那么就进行网络请求
->加载到的资源缓存到硬盘和内存
例如:访问图片-> 200 -> 退出浏览器
再进来-> 200(from disk cache) -> 刷新 -> 200(from memory cache)
接下来我们谈一谈iOS中使用到的缓存,其实我们最常用的就是ibireme开发的YYKit中的YYCache,这里我们来谈一谈!
1.YYCache的设计思路
首先内存缓存,比较了一些官方以及第三方的库,例如苹果的NSCache,这里可以浅谈一下NSCache,毕竟在AFNetworking以及SDWebImage中都是使用的它.
简单说明:NSCache
(1)一旦调用了 removeAllObjects,就无法给 cache 添加对象。
(2)关于 NSCache 的内存管理,交给他自己就行!
(3)SDWebImage 中存在同样的问题,一旦内存警告,清理了内存之后,之后所有的图片都是从沙盒加载的。
(4)NSCache 是线程安全的,在多线程操作中,不需要对 Cache 加锁。
(5)NSCache 的 Key 只是做强引用,与可变字典不同,缓存对象不会对键名做 copy 操作,不需要实现 NSCopying协议
YYMemoryCache 是作者开发的一个内存缓存,相对于 PINMemoryCache 来说,去掉了异步访问的接口,尽量优化了同步访问的性能,用 OSSpinLock 来保证线程安全。另外,缓存内部用双向链表和 NSDictionary 实现了 LRU 淘汰算法,相对于其他的几个库而言,写入和读取的速度都有提升。
其次磁盘缓存:通过观察其他的开源库,PINDiskCache,NSURLCache等,技术实现分为三种:基于文件读写,基于mmap文件内存映射,基于数据库。通过对比很明显基于数据库的明显要优于其他的性能。
YYDiskCache也是使用的SQLite配合文件的存储方式,单条数据大于20K的时候,采用的文件的方式,而文件较小的时候就采用的是sqlite的方式读取。
2.结构图
YYCache:提供了最外层的接口,调用了YYMemoryCache与YYDiskCache的相关方法。
YYMemoryCache:负责处理容量小,相对高速的内存缓存。线程安全,支持自动和手动清理缓存等功能。
_YYLinkedMap:YYMemoryCache使用的双向链表类。
**YYLinkedMapNode:是YYLinkedMap使用的节点类。
YYDiskCache:负责处理容量大,相对低速的磁盘缓存。线程安全,支持异步操作,自动和手动清理缓存等功能。
YYKVStorage:YYDiskCache的底层实现类,用于管理磁盘缓存。
YYKVStorageItem:内置在YYKVStorage中,是YYKVStorage内部用于封装某个缓存的类
3.代码讲解
(1)YYCache中,永远都是先访问内存缓存,然后再访问磁盘缓存(包括了写入,读取,查询,删除缓存的操作)
- (void)setObject:(id)object forKey:(NSString * )key {
//先写入内存缓存,后写入磁盘缓存
[ _memoryCache setObject:object forKey:key];
[ _diskCache setObject:object forKey:key];
}
- (void)removeObjectForKey:(NSString * )key {
//先移除内存缓存,后移除磁盘缓存
[ _memoryCache removeObjectForKey:key];
[ _diskCache removeObjectForKey:key];
}
(2)在读取缓存的操作中,如果在内存缓存中无法获取对应的缓存,则会去磁盘缓存中寻找。如果在磁盘缓存中找到了对应的缓存,则会将该对象再次写入内存缓存中,保证在下一次尝试获取同一缓存时能够在内存中就能返回,提高速度。
- (id)objectForKey:(NSString * )key {
//首先尝试获取内存缓存,然后获取磁盘缓存
id object = [_memoryCache objectForKey:key];
//如果内存缓存不存在,就会去磁盘缓存里面找:如果找到了,则再次写入内存缓存中;如果没找到,就返回nil
if (!object) {
object = [_diskCache objectForKey:key];
if (object) {
[_memoryCache setObject:object forKey:key];
}
}
return object;
}
(3)YYMemoryCache内部使用到了LRU算法以及缓存清理策略
缓存淘汰算法:使用LRU(least-recently-used) 算法来淘汰(清理)使用频率较低的缓存。具体的实现是采用的双向链表
缓存清理策略:使用三个维度来标记,分别是count(缓存数量),cost(开销),age(距上一次的访问时间)。YYMemoryCache提供了分别针对这三个维度的清理缓存的接口。用户可以根据不同的需求(策略)来清理在某一维度超标的缓存
大家如果有兴趣,可以去看一下源码,了解一下作者的双向链表是如何实现这个淘汰算法的。
参考资料: