先上源码链接https://github.com/ibireme/YYCache
简介
YYCache 是提供用用户使用的对象,内部对 YYMemoryCache
和 YYDiskCache
功能的整合封装。为 YYMemoryCache
提供了多线程功能,而YYDiskCache
对象本身内部封装了异步读写功能。
YYCache分为两部分:磁盘缓存、内存缓存。
内存缓存(YYMemoryCache)
存储的单元是_YYLinkedMapNode
,除了key和value外,还存储了它的前后Node的地址_prev、_next
。整个实现基于_YYLinkedMap
,它是一个双向链表,除了存储了字典_dic外,还存储了头结点和尾节点。它实现的功能很简单:有新数据了插入链表头部,访问过的数据结点移到头部,内存紧张时把尾部的结点移除。就这样实现了淘汰算法。因为内存访问速度很快,锁占用的时间少,所以用的速度最快的OSSpinLockLock
硬盘缓存(YYDiskCache)
采用 文件和数据库相互配合的方式。有一个参数inlineThreshold(默认值20KB)
,小于它存数据库,大于它存文件,能获得效率的提高。key:path,value:cache
存储在NSMapTable
里,根据path获得cache,进行一系列的set、get、remove操作。
更底层的是YYKVStorage
,它能直接对sqlite和文件系统进行读写。每次内存超过限制时,select key, filename, size from manifest order by last_access_time desc limit ?1
会根据时间排序,来删除最近不常用的数据。硬盘访问的时间比较长,如果用OSSpinLockLock
锁会造成CPU消耗过大,所以用的dispatch_semaphore_wait
来做。
基本使用
一、同步方式存取
1.初始化缓存对象
- (nullable instancetype)initWithName:(NSString *)name;
- (nullable instancetype)initWithPath:(NSString *)path;
+ (nullable instancetype)cacheWithName:(NSString *)name;
+ (nullable instancetype)cacheWithPath:(NSString *)path;
这几个方法功能是一致的。
如:
YYCache *cache = [[YYCache alloc] initWithName:@"APP_CACHE"];
2.存
id data; // 需要存储的数据
YYCache *cache = [YYCache cacheWithName:@"APP_CACHE"];
[cache setObject:data forKey:@"data_key"];
首先调用内存缓存设置缓存API(setObject:forKey:)
,然后调用磁盘缓存异步设置API(setObject:forKey:)
3.判断是否有缓存
YYCache *cache = [YYCache cacheWithName:@"APP_CACHE"];
BOOL isContains = [cache containsObjectForKey:@"data_key"]
先去内存缓存中查找,如果没有查找就去磁盘缓存中找,找到就返回 YES。
4.取
YYCache *cache = [YYCache cacheWithName:@"APP_CACHE"];
id data = [cache objectForKey:@"data_key"];
先去内存缓存中查找,有就返回。如果没有就去磁盘缓存中找,找到了,先把缓存存入内存缓存中,然后再返回结果。
5.移除缓存
YYCache *cache = [YYCache cacheWithName:@"APP_CACHE"];
//根据key移除缓存
[cache removeObjectForKey:@"data_key"];
//移除APP所有缓存
[cache removeAllObjects];
首先调用内存缓存的删除接口(removeObjectForKey:)
,然后调用磁盘缓存的删除接口(removeObjectForKey:)
二、异步方式
存
YYCache *cache = [YYCache cacheWithName:@"APP_CACHE"];
id data; // 需要存储的数据
[cache setObject:data forKey:@"data_key" withBlock:^{
NSLog(@"save sucess");
}];
首先调用内存缓存设置缓存API(setObject:forKey:)
,然后调用磁盘缓存异步设置API(setObject:forKey:)
判断缓存是否存在
[cache containsObjectForKey:@"data_key" withBlock:^(NSString * _Nonnull key, BOOL contains) {
if(contains){
//...
}
}];
同步在内存中查找缓存,如果找到了,使用异步返回。如果没找到,就调用磁盘缓存的异步查找 API,并把查找结果异步回调。
取
[cache objectForKey:@"data_key" withBlock:^(NSString * _Nonnull key, id _Nonnull object) {
if(object){
// 获取成功
}
}];
同步在内存中查找,如果找到了,使用异步返回。如果没找到,就调用磁盘缓存的异步查找 API,找到了,先把缓存存入内存缓存中,然后再返回结果。
移除
//根据key移除缓存
[cache removeObjectForKey:@"data_key" withBlock:^(NSString * _Nonnull key) {
NSLog(@"removeObjectForKey: %@",key);
}];
//移除APP所有缓存
[cache removeAllObjectsWithBlock:^{
NSLog(@"removeAllObjects sucess");
}];
首先调用内存缓存删除接口(removeObjectForKey:)
,然后调用磁盘缓存异步删除接口(removeObjectForKey:withBlock:)
移除所有缓存带进度
[cache removeAllObjectsWithProgressBlock:^(int removedCount, int totalCount) {
NSLog(@" 已移除数 :%d , 移除总数: %d",removedCount,totalCount);
} endBlock:^(BOOL error) {
if(!error){
NSLog(@"sucess");
}else{
NSLog(@"error");
}
}];
缓存/ 磁盘单独操作
YYCache *cache = [YYCache cacheWithName:@"APP_CACHE"];
YYDiskCache *diskCache = cache.diskCache;
YYMemoryCache *memoryCache = cache.memoryCache;
存取方法与cache 一致
缓存LRU清理
LRU(Least Recently Used)
算法翻译过来就是“最近最少使用”。LRU缓存原理,简单的说就是,缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉。
比如缓存10000条数据,当数据小于10000时可以随意添加;当超过10000时,就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条。
那怎么确定删除哪条过期数据?采用LRU算法实现的话就是将最老的数据删掉。
YYCache磁盘LRU清理并不是及时清理,而是后台开启一个定时任务进行RLU清理操作,定时默认是60s。
YYCache *yyCache=[YYCache cacheWithName:@"LCJCache"];
[yyCache.memoryCache setCountLimit:50]; //内存最大缓存数据个数
[yyCache.memoryCache setCostLimit:1*1024];//内存最大缓存开销 目前这个毫无用处
[yyCache.diskCache setCostLimit:10*1024];//磁盘最大缓存开销
[yyCache.diskCache setCountLimit:50]; //磁盘最大缓存数据个数
[yyCache.diskCache setAutoTrimInterval:60];//设置磁盘lru动态清理频率 默认 60秒
相关资料
https://blog.ibireme.com/2015/10/26/yycache/
https://www.cnblogs.com/machao/p/7086675.html
https://www.jianshu.com/p/5a63bdca4a21