• socket协议下如何缓存图片--推荐EGOCache


    EGOCache是一个轻量级的缓存框架。用法简单方便,在现在的项目中,我就用到EGOCache来缓存下载过的照片和字符串。

    有人可能会问到,缓存照片还需要用EGOCache吗?AFNetworking和SDWebImage不是已经有这些功能了吗?

    是的,不过AFNetworking和SDWebImage是http。我的项目用的是socket,所以我选择EGOCache来做缓存。用下来觉得EGOCache还是挺强大的。

    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 : https://github.com/enormego/EGOCache

     

    EGOCache可以设定缓存过期时间,默认是1天。查看了一下EGOCache源码,设置默认时间:

        [self setDefaultTimeoutInterval:86400];
    
        //86400 = 24 * 60 * 60 刚好是一天时间。

    EGOCache为什么要提供设定缓存过期时间呢?或者说设定缓存过期时间有什么好处呢?我觉得最大的好处就是可以定时清除缓存。可以设置某一项的缓存时间,很方便管理缓存。

    那么问题来了:

    1. EGOCache是怎么检测缓存过期时间的呢?

    2. 检测到时间过期之后,什么时候触发删除缓存项的?

    带着这两个问题,我们来继续分析。

    你会怎么实现

    记得在公司里,老板经常会举这样的例子:

    某某同志,刚来我们公司的时候,遇到问题就知道抱怨。从来不知道去思考怎么解决,只知道把问题抛给领导。工作半年下来,成长了很多。现在碰到问题,不仅把问题抛出来,而且还提供了自己的解决方案...

    类似的例子,相信大家都听过。同样,既然前面我们提出这两个问题,我们也先来思考一下,如果我们来做该怎么解决?

    如果让我来写的话,我脑海里初步实现方法有几个:

    1. 通过定时器来轮询,每隔一段时间检测一次。

    2. 写一个while循环来检测。

    3. 每次去读取缓存项的时候,判断缓存时间有没有过期。没过期,就返回读取的缓存项;否则,返回nil。

    当然,还有一些方法,不一一例举了。仔细想想,这些方法弊端很容易显露出来。

    1. 为了小小的缓存时间,就用定时器轮询,显然是资源浪费

    2. 跟方法1差不多。

    3. 每次读取的时候判断是否过期,如果一直不读取,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;
    }

    每次初始化的时候去判断了缓存项是否过期,这样做非常正确。思考一个场景:

    1. 用户打开app,EGOCache被初始化,并判断了缓存项是否过期。

    2. 如果刚好有一些缓存项在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检测缓存时间过期的思路值得学习,以后遇到类似场景,完全可以借鉴。

  • 相关阅读:
    static关键字总结
    C、C++的内存分区与存储
    new、delete、malloc、free、realloc的区别
    Python3连接MySQL数据库之mysql-client
    Linux系统配置
    Windows环境下进行socket编程
    jQuery学习-事件之绑定事件(七)
    jQuery学习-事件之绑定事件(六)
    jQuery学习-事件之绑定事件(五)
    jQuery学习-事件之绑定事件(四)
  • 原文地址:https://www.cnblogs.com/yunxinxi/p/4668855.html
Copyright © 2020-2023  润新知