iOS缓存配置、AFN缓存配置

看了很多文章,大部分都是说缓存怎么读取,而缓存的写入都说是系统自动写入,说的不清楚。下面我就自己看过的文章和自己的实践理解,写一篇关于NSURLRequest、NSURLSession和AFNetworking缓存的介绍。

学习iOS的缓存主要有两个方面,数据写入缓存读取,下面先讲缓存读取

缓存读取

只需要设置request请求的cachePolicy属性,就可以控制缓存的读取,很简单。

  • 常用缓存策略
//使用协议缓存策略,也就是按照响应头的HTTP缓存字段来使用缓存
NSURLRequestUseProtocolCachePolicy
//忽略缓存
NSURLRequestReloadIgnoringLocalCacheData
//有缓存则读缓存(就算缓存过期也读取),没有则网络加载
NSURLRequestReturnCacheDataElseLoad
//有缓存则读缓存(就算缓存过期也读取),没有则报错
NSURLRequestReturnCacheDataDontLoad
  • 下面有三种方法配置request的缓存策略,而且生效优先级依次降低

1、设置单个请求的缓存策略,直接设置单个请求的缓存策略,其生效的优先级是最高的,不会受到全局缓存配置的影响。

NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;//本次请求忽略本地缓存 重新请求数据

2、设置NSURLSession缓存策略,使用该session发出的所有请求都会服从该session的缓存策略,其生效优先级低于上面的单个请求配置

NSURLSessionConfiguration *confi = [NSURLSessionConfiguration defaultSessionConfiguration];
confi.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
NSURLSession *session = [NSURLSession sessionWithConfiguration:confi delegate:self 
                                                 delegateQueue:[NSOperationQueue mainQueue]];

3、设置AFNetworking缓存策略,生效优先级低于上面的NSURLSession缓存配置

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

注意点:如果允许使用缓存,在没有网络的情况下,倘若本地有缓存则会走成功的回调返回缓存数据,不会走请求失败的回调

缓存写入

缓存的写入所需的条件比较多

  • 系统默认使用NSURLCache做缓存容器,首先我们可以按需设置NSURLCache的大小,我这里设置了4兆的内存20兆的磁盘空间做缓存。如果不需要缓存那么直接设置空间为0.
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 
                                            diskCapacity:20 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];    //设置共享缓存容器
  • 缓存在写入前会调用NSURLSession的willCacheResponse这个代理方法,并且只有将response传给completionHandler()之后系统才会缓存该响应,如果传入nil则表示不缓存该响应。我们可以在这个方法里修改响应信息,然后保存修改之后的响应信息。(不只是GET请求,POST请求也能触发代理方法,保存响应)
  • 响应信息会被缓存下来的先决条件:首先要满足willCacheResponse代理方法的触发条件(如果这个代理方法都不能触发,那么缓存的写入也无从谈起)。然后将需要缓存的响应传给代理方法的completionHandler()进行保存,如果传入nil则表示不缓存。
    注意:如果设置共享缓存空间为0,那么也不会缓存响应信息

只有满足以下所有条件时才会触发willCacheResponse代理方法:
1、请求是针对HTTP或HTTPS URL(或你自己的支持缓存的自定义网络协议)。
2、请求成功(状态码在200-299范围内)。
3、返回的响应数据来自服务器,而不是来自本地缓存。(废话,本地缓存不会再缓存一遍)
下面两个条件是我从其他文章看到的,经过测试发现并不需要
//4、session会话配置的缓存策略允许缓存。(这个条件好像有问题,会话配置只决定是否使用缓存,而不决定缓存的写入)
//5、NSURLRequest对象的缓存策略(如果适用)允许缓存。(这也是控制响应的写入)
6、服务器响应中的缓存相关头(如果存在)允许缓存。也就是响应头中的HTTP缓存字段(这里一下解释不清,请自行查看http的缓存相关字段)
7、响应大小足够小,可以合理地放入缓存中。 (例如,如果您提供磁盘缓存,则响应不得超过磁盘缓存大小的5%。)
注:如果代理实现此方法willCacheResponse,则该代理方法中必须调用completionHandler完成处理程序,否则应用程序会泄漏内存。

  • 对willCacheResponse这个代理方法的一些使用
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask 
    willCacheResponse:(NSCachedURLResponse *)proposedResponse 
    completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {

    //这里可以修改HTTP的缓存字段
    NSURLResponse *response = proposedResponse.response;
    NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse*)response;
    NSDictionary *headers = HTTPResponse.allHeaderFields;
    NSCachedURLResponse *cachedResponse;
    //修改Cache-Control字段的值
    NSMutableDictionary *modifiedHeaders = headers.mutableCopy;
    [modifiedHeaders setObject:@"max-age=10" forKey:@"Cache-Control"];
    NSHTTPURLResponse * modifiedResponse;
    modifiedResponse = [[NSHTTPURLResponse alloc] initWithURL:HTTPResponse.URL 
                            statusCode:HTTPResponse.statusCode HTTPVersion:@"HTTP/1.1" 
                            headerFields:modifiedHeaders];
    cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:modifiedResponse 
                            data:proposedResponse.data
                            userInfo:proposedResponse.userInfo 
                            storagePolicy:proposedResponse.storagePolicy];

    //将修改后的响应信息交给该block执行,该block就会保存该响应,当然了也可以不修改响应
    completionHandler(cachedResponse);   //返回nil则表示不缓存响应
}

AFN缓存

AFN默认已经实现了willCacheResponse代理方法,只要满足这个代理方法的触发条件,那么该响应就会被缓存到本地。
并且如果在缓存响应之前想要修改这个响应,可以设置AFN的一个block,在这个block中修改响应信息,如下。(当然也可以在这个block中设置过滤,根据请求信息过滤掉不想缓存的响应)

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager setDataTaskWillCacheResponseBlock:^NSCachedURLResponse * _Nonnull(NSURLSession * _Nonnull session, 
           NSURLSessionDataTask * _Nonnull dataTask, NSCachedURLResponse * _Nonnull proposedResponse) {
    //修改响应信息
    NSURLResponse *response = proposedResponse.response;
    NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse*)response;
    NSDictionary *headers = HTTPResponse.allHeaderFields;
    NSCachedURLResponse *cachedResponse;
    //这里就可以针对Cache-Control进行更改,然后直接我们通过某方法获取NSCachedURLResponse的时候就可以先去判断下头域信息
    NSMutableDictionary *modifiedHeaders = headers.mutableCopy;
    [modifiedHeaders setObject:@"max-age=1000" forKey:@"Cache-Control"];
    NSHTTPURLResponse *modifiedResponse = [[NSHTTPURLResponse alloc] initWithURL:HTTPResponse.URL statusCode:
                                                   HTTPResponse.statusCode HTTPVersion:@"HTTP/1.1" headerFields:modifiedHeaders];
    cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:modifiedResponse data:proposedResponse.data
                                                 userInfo:proposedResponse.userInfo storagePolicy:proposedResponse.storagePolicy];
            
    return cachedResponse;      //返回修改后的响应
}];

如果想要AFN使用已经保存好的响应,那么直接设置AFN的缓存策略允许使用缓存即可,几种缓存使用策略上面已经介绍过了

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

通常在实际应用中需要根据不同的网络状态设置不同的缓存策略,一般对于JSON数据来说,因为它通常是非资源文件,可能会经常变动,所以在有网的情况下需要禁用缓存来实时刷新,在无网的情况下才使用缓存数据。通常可以配合AFN的网络监听一起使用,以达到在不同的网络环境下使用不同的缓存策略的效果。

//使用AFN框架来检测网络状态的改变
-(void)AFNReachability{
    /*
     AFNetworkReachabilityStatusUnknown     = 未知
     AFNetworkReachabilityStatusNotReachable   = 没有网络
     AFNetworkReachabilityStatusReachableViaWWAN = 3G
     AFNetworkReachabilityStatusReachableViaWiFi = WIFI
     */
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
                break;
            case AFNetworkReachabilityStatusNotReachable:
                self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                self.manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                self.manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
                break;
            default:
                self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
                break;
        }
    }];
    //开始监听
    [manager startMonitoring];
}

GET、POST使用缓存时的区别

系统缓存都是存放在以下路径的数据库文件中,注意显示隐藏文件,因为文件夹的名字是AppID,有些AppID是以"."点开头的,系统会将以点开头的文件当做隐藏文件

数据库文件中就存放着我们的缓存数据,并且以请求的URL为键值进行保存,读取缓存时也是系统自动通过URL读取。当请求没有参数或者参数值固定不变时,GET和POST请求都能够正确读到缓存,但是当请求有参数,并且参数的值不固定时,POST使用缓存就有问题了,你会发现无论参数值如何变化,获取到的缓存数据都是一样的。这是因为POST请求的参数没有拼接在URL后面,所以导致参数值无论怎么变,URL始终是一样的,以URL为键值获取的缓存数据也是一样(因为URL一样,所以系统每次请求都会覆盖上一次缓存)。而GET请求不一样,他的参数拼接在URL后面,所以当参数值变化时URL也随之改变,不同的参数值唯一确定不同的一个URL,所以读取的缓存也就一一对应,不会重复。

总结一下:

  • 请求没有参数或者参数值固定不变,那么GET和POST都可以正常使用缓存
  • 请求有参数且参数值会变化,那么GET可以正常使用缓存,POST不可以

参考文章
iOS网络——NSURLCache设置网络请求缓存
NSURLSession发送请求通过NSURLCache 做的缓存
NSURLSession 所有的都在这里(二)
https://www.cnblogs.com/rainySue/p/huan-cun.html#toc_11

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容