IOS缓存管理之PINCache使用
前言:
今年重点在于公司iOS架构的梳理工作,上周整理了http请求接口管理与解耦,接下来准备整理一下项目中的缓存处理,目前项目中使用的是PINCache,去年加入这个开源框架时并没有对这个框架进行了解,导致现在同步方式异步方式的使用存在一定的混乱情况和错误使用现象。今天重新站在使用者的角度对这个再做一次了解,以避免在后期的使用中出现类似以往的问题。
关于缓存:
无论是Android还是IOS都会使用到缓存,缓存的设计方案也大致雷同(内存缓存+磁盘缓存),内存缓存方面Android采用LinkedHashMap,IOS采用NSDictionary,两者都是基于Key-Value模型进行存储,磁盘缓存方面Android采用写文件,IOS这边采用归档操作本质是也是写文件。
关于PINCache
PINCache是Pinterest的程序员在Tumblr的TMCache基础上发展而来的,TMCache已经不再维护,PINCache主要的改进是修复了dealock的bug,是一个快速,无死锁的并行对象缓存,支持 iOS 和 OS X 系统。上面已经了解到PINCache分两层缓存(内存缓存+磁盘缓存),内部实现主要有两个类实现:PINMemoryCache、PINDiskCache,同时对两者提供了同步异步调用方式,由于磁盘缓存采用的是归档操作,所以对自定义的对象必须实现NSCoding协议,PINCache除了可以按键取值、按键存值、按键删值之外,还可以移除某个日期之前的缓存数据、删除所有缓存、限制缓存大小,限制缓存对象的存活时间等。接下来看下具体使用方式。
PINCache使用
同步方式
//模拟数据 NSString *value=@"who is lcj"; //模拟一个key NSString *key=@"whoislcj"; //sync 同步方式 //写入缓存 [[PINCache sharedCache] setObject:value forKey:key]; //判断缓存是否存在 BOOL containsObject =[[PINCache sharedCache] containsObjectForKey:key]; NSLog(@"containsObject : %@", containsObject?@"YES":@"NO"); //根据key读取数据 id vuale=[[PINCache sharedCache] objectForKey:key]; NSLog(@"value : %@",vuale); //根据key移除缓存 [[PINCache sharedCache]removeObjectForKey:key]; //移除所有缓存 [[PINCache sharedCache]removeAllObjects]; //根据截至日期 清除缓存 [[PINCache sharedCache]trimToDate:[NSDate date]];
异步方式
//模拟数据 NSString *value=@"who is lcj"; //模拟一个key NSString *key=@"whoislcj"; //async 异步方式 //写入数据 [[PINCache sharedCache] setObject:value forKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { NSLog(@"value : %@",object); }]; //判断缓存是否存在 [[PINCache sharedCache]containsObjectForKey:key block:^(BOOL containsObject) { NSLog(@"isContains : %@", containsObject?@"YES":@"NO"); }]; //根据key读取数据 [[PINCache sharedCache]objectForKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { NSLog(@"value : %@",object); }]; //根据key移除缓存 [[PINCache sharedCache]removeObjectForKey:key block:^(PINCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { NSLog(@"value : %@",cache); }]; //移除都有缓存 [[PINCache sharedCache]removeAllObjects:^(PINCache * _Nonnull cache) { NSLog(@"value : %@",cache); }]; //根据截至日期 清除缓存 [[PINCache sharedCache]trimToDate:[NSDate date] block:^(PINCache * _Nonnull cache) { NSLog(@"value : %@",cache); }];
PINCache使用起来还是非常容易的。
PINCache缓存LRU清理机制
LRU(Least Recently Used)算法大家都比较熟悉,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现的话就是将最老的数据删掉。接下来我们测试一下,
PINCache *pinCache=[PINCache sharedCache]; //模拟内存使用最大开销限制是1k [pinCache.memoryCache setCostLimit:1*1024]; //模拟磁盘使用最大开销限制是10k [pinCache.diskCache setByteLimit:1024*10];
模拟500条数据进行存储
for(int i =0;i<500;i++){ //模拟数据 NSString *value=@"I want to know who is lcj ?"; //模拟一个key NSString *key=[NSString stringWithFormat:@"whoislcj:%d",i]; //写入数据 [pinCache setObject:value forKey:key]; } NSLog(@"pinCache.memoryCache.totalCost:%lu",(unsigned long)pinCache.memoryCache.totalCost); NSLog(@"pinCache.memoryCache.costLimit:%lu",(unsigned long)pinCache.memoryCache.costLimit); NSLog(@"pinCache.diskCache.byteCount:%lu",(unsigned long)pinCache.diskCache.byteCount); NSLog(@"pinCache.diskCache.byteLimit:%lu",(unsigned long)pinCache.diskCache.byteLimit);
通过上述的运行结果会发现,diskCache起作用了,memoryCache并没有起作用,我一步步跟进源码查看跟踪到memoryCache.m这段代码
- (void)setObject:(id)object forKey:(NSString *)key { [self setObject:object forKey:key withCost:0]; }
由于每次保存内存存进去的cost开销都是0,由于导致totalCost累计也是0,与costLimit对比永远无法进行内存lru处理,声明一下:我们用的版本是2.3版本,最新的3.0.1-beta.2也有同样的问题,是PINCache没有实现内存LRU还是这仅仅是一个bug就不得而知了。
总结:
内部源码大致看了一遍,大致明白内部实现原理,由于理解不够深刻,就不对其源码进行分析了,大致了解一下具体使用方式。