Objective-C的缓存框架EGOCache在iOS App开发中的使用
egocache简介
egocache is a simple, thread-safe key value cache store. it has native support for nsstring, ui/nsimage, and nsdata, but can store anything that implements <nscoding>. all cached items expire after the timeout, which by default, is one day.
翻译过来就是:egocache一个简单、线程安全的基于 key-value 的缓存框架,原生支持nsstring、ui/nsimage、和nsdata,也支持储存任何实现协议的类,可以设定缓存过期时间,默认是1天。
egocache只有一个类,egocache.h和egocache.m两个文件。用法也比较容易掌握,仔细研究一下egocache.h的方法,很快就可以上手。
egocache只提供了磁盘缓存,没有提供内存缓存。同时,也提供了清理缓存的方法:
- (void)clearcache;
egocache还提供了判断缓存是否存在的方法:
- (bool)hascacheforkey:(nsstring* __nonnull)key;
通过cocoapods直接加入项目
直接在你的项目的podfile加入下面一行:
pod 'egocache'
然后执行:
$ pod update
egocache用法
用egocache缓存nsstring
存储:
nsstring *savestring = @"把我保存起来吧";
[[egocache globalcache] setstring:savestring forkey:[nsstring stringwithformat:@"egoimageloader-%lu", (unsigned long)[savestring hash]] withtimeoutinterval:24*60*60];
读取:
nsstring *getsavestring = [[egocache globalcache] stringforkey:[nsstring stringwithformat:@"egoimageloader-%lu", (unsigned long)[@"savestring" hash]]];
是不是感觉跟nsdictionary很相似,确实,前面我们说了egocache是基于key-value 的缓存框架。
用egocache缓存uiimage
存储:
uiimage *saveimage = [uiimage imagenamed:@"iosdevtip"];
[[egocache globalcache] setimage:saveimage forkey:[nsstring stringwithformat:@"egoimageloader-%lu", (unsigned long)[@"saveimage" hash]] withtimeoutinterval:24*60*60];
读取:
uiimage *getsaveimage = [[egocache globalcache] imageforkey:[nsstring stringwithformat:@"egoimageloader-%lu", (unsigned long)[@"saveimage" hash]]];
用egocache缓存nsdata
存储:
nsdata *savedata = [nsdata data];
[[egocache globalcache] setdata:savedata forkey:[nsstring stringwithformat:@"egoimageloader-%lu", (unsigned long)[@"savedata" hash]] withtimeoutinterval:24*60*60];
读取:
uiimage *getsavedata = [[egocache globalcache] dataforkey:[nsstring stringwithformat:@"egoimageloader-%lu", (unsigned long)[@"savedata" hash]]];
egocache如何检测缓存时间过期
egocache可以设定缓存过期时间,默认是1天。查看了一下egocache源码,设置默认时间:
[self setdefaulttimeoutinterval:86400];
//86400 = 24 * 60 * 60 刚好是一天时间。
egocache为什么要提供设定缓存过期时间呢?或者说设定缓存过期时间有什么好处呢?我觉得最大的好处就是可以定时清除缓存。可以设置某一项的缓存时间,很方便管理缓存。
那么问题来了:
- egocache是怎么检测缓存过期时间的呢?
- 检测到时间过期之后,什么时候触发删除缓存项的?
带着这两个问题,我们来继续分析。
你会怎么实现
记得在公司里,老板经常会举这样的例子:
某某同志,刚来我们公司的时候,遇到问题就知道抱怨。从来不知道去思考怎么解决,只知道把问题抛给领导。工作半年下来,成长了很多。现在碰到问题,不仅把问题抛出来,而且还提供了自己的解决方案...
类似的例子,相信大家都听过。同样,既然前面我们提出这两个问题,我们也先来思考一下,如果我们来做该怎么解决?
如果让我来写的话,我脑海里初步实现方法有几个:
- 通过定时器来轮询,每隔一段时间检测一次。
- 写一个while循环来检测。
- 每次去读取缓存项的时候,判断缓存时间有没有过期。没过期,就返回读取的缓存项;否则,返回nil。
- 当然,还有一些方法,不一一例举了。仔细想想,这些方法弊端很容易显露出来。
为了小小的缓存时间,就用定时器轮询,显然是资源浪费
跟方法1差不多。
每次读取的时候判断是否过期,如果一直不读取,app的缓存会越来越大,也不可取。
这些方法都被排除了,还有好的方法吗?继续往下看:
egocache是怎么实现的?
仔细查看egocache源码,发现在initwithcachedirectory:方法里,每次初始化egocache实例对象的时,会遍历一遍plist文件中所有已存在的缓存项,拿每个缓存项的时间和当前时间作比较,缓存项过期时间早于当前时间,则删除对应缓存文件,并删除 plist 文件中对应 key 的记录。
具体实现代码如下:
读取缓存项信息
_cacheinfo = [[nsdictionary dictionarywithcontentsoffile:cachepathforkey(_directory, @"egocache.plist")] mutablecopy];
if(!_cacheinfo) {
_cacheinfo = [[nsmutabledictionary alloc] init];
}
获取当前时间的nstimeinterval
nstimeinterval now = [[nsdate date] timeintervalsincereferencedate];
声明removedkeys保存过期的缓存项对应的key
nsmutablearray* removedkeys = [[nsmutablearray alloc] init];
遍历缓存项信息并判断缓存时间
for(nsstring* key in _cacheinfo) {
//判断缓存项过期时间是否早于当前时间
if([_cacheinfo[key] timeintervalsincereferencedate] <= now) {
//如果缓存项过期时间早于当前时间,移除缓存项
[[nsfilemanager defaultmanager] removeitematpath:cachepathforkey(_directory, key) error:null];
//把过期的缓存项对于的key保存到removedkeys里面
[removedkeys addobject:key];
}
}
删除过期缓存项对于的key
[_cacheinfo removeobjectsforkeys:removedkeys];
看到这些,是不是觉得人家思路特牛叉,反正,我是觉得这个作者不简单。到这一步就解决了吗?
egocache还做了什么?
细心的童鞋会发现:egocache是个单列类,也就是说整个程序应用周期只初始化一次。
+ (instancetype)globalcache {
static id instance;
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, ^{
instance = [[[self class] alloc] init];
});
return instance;
}
每次初始化的时候去判断了缓存项是否过期,这样做非常正确。思考一个场景:
用户打开app,egocache被初始化,并判断了缓存项是否过期。
如果刚好有一些缓存项在egocache被初始化之后过期。这个时候我们依然可以读到这个缓存项。这就不对了。
继续分析egocache源码发现,egocache在读取一个缓存项的时候,先判断缓存项是否存在,然后读取缓存项(注意:是读取egocache初始化的时候没有过期的缓存项,并没有说现在没有过期),最后去判断读取到的缓存项跟当前时间相比是否过期.
具体实现如下:
- (bool)hascacheforkey:(nsstring*)key {
//读取egocache初始化的时候没有过期的缓存项
nsdate* date = [self dateforkey:key];
if(date == nil) return no;
//判断读取到的缓存项当前是否过期
if([date timeintervalsincereferencedate] < cfabsolutetimegetcurrent()) return no;
return [[nsfilemanager defaultmanager] fileexistsatpath:cachepathforkey(_directory, key)];
}
- (nsdate*)dateforkey:(nsstring*)key {
__block nsdate* date = nil;
dispatch_sync(_frozencacheinfoqueue, ^{
date = (self.frozencacheinfo)[key];
});
return date;
}
egocache检测缓存时间过期的思路值得学习,以后遇到类似场景,完全可以借鉴。
推荐阅读
-
iOS App开发中的UIStackView堆叠视图使用教程
-
iOS App开发中UIViewController类的使用教程
-
iOS App设计模式开发中对迭代器模式的使用示例
-
iOS App开发中的UIPageControl分页控件使用小结
-
实例解析设计模式中的外观模式在iOS App开发中的运用
-
iOS App使用设计模式中的模板方法模式开发的示例
-
实例讲解设计模式中的命令模式在iOS App开发中的运用
-
设计模式中的Memento备忘录模式的在iOS App开发中的运用
-
Objective-C的缓存框架EGOCache在iOS App开发中的使用
-
iOS App开发中使用设计模式中的单例模式的实例解析