前言
前面一篇文章HTTP协议之缓存介绍了HTTP协议中缓存是如何定义,客户端,服务端一般要如何根据HTTP中定义的这些字段来实现缓存逻辑
接下来要着重分析一下IOS里面的网络框架,是如何进行网络缓存的
本文使NSURLSession进行网络请求
用什么缓存
ios中的网络缓存主要用到 NSURLCache,结合NSURLSession,NSURLRequest,系统已经为我们做了大部分工作。
NSURLCache将缓存保存到应用程序沙盒Library/Caches/xxx.xxx/路径下
用数据库来保存每个接口的缓存数据。
为什么要用数据库保存而不是以请求做文件名,以请求内容做文件内容保存,因为NSURLCache不光要保存请求的数据,还要保存很多额外的信息
- 响应头信息(各种Cache-Control字段等等)
- 缓存的版本
- 时间戳
- 一些其他的控制信息
这些字段用来对缓存进行更好的控制,比如当缓存超过设置空间上限的时候,清理缓存的策略等等

如何启用,禁用缓存
启用缓存
系统已经为我们做了足够多的工作,我们只需要很少的步骤就能启用NSURLCache这套缓存系统
如果我们使用系统默认的NSURLSession *session=[NSURLSession sharedSession];那么我们实际上不需要配置任何代码,因为系统默认的全局NSURLSession的NSURLSessionConfiguration里面已经默认配置了一个NSURLCache对象,这个NSURLCache对象也是一个系统默认的全局对象,可以通过NSURLCache *cache=[NSURLCache sharedURLCache];获得
这样我们通过NSURLRequest成功返回的GET请求都会被缓存,注意请求被缓存与我们设置的NSURLRequest的cachePolicy无关
当然我们也可以自己创建一个NSURLCache,自定义内存以及硬盘缓存的容量,并绑定到全局使用
如下代码创建了一个缓存配置,并设置为全局共享缓存配置
1 | //新建一个缓存配置 |
修改NSURLSessionConfiguration使用的缓存对象
默认的NSURLSessionConfiguration使用的是系统创建的NSURLCache或者我们通过[NSURLCache setSharedURLCache:cache]配置的cache,我们也可以修改config的URLCache属性来直接指定一个NSURLCache对象
1 | //新建一个缓存配置 |
禁用缓存
缓存的启用是通过为NSURLSessionConfiguration配置一个NSURLCache对象(默认的session,默认的config使用的是默认的NSURLCache对象),那么缓存的禁用可以置空这个session的config.URLCache属性,这样这个session的所有请求都不会被缓存
1 | NSURLSession *session=[NSURLSession sharedSession]; |
这种方式对这个session中的所有请求都生效,如果我们想只是针对单独的某个请求,可以实现NSURLSession 的delegate
1 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask |
当某个请求将要被缓存的时候,会调用此delegate,在这里我们可以修改将要被缓存的response,同时决定是否缓存这个请求。如下代码修改response中的Cache-Control字段并缓存请求。
1 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask |
如果不想缓存请求 调用completionHandler(nil)即可
IOS CachePolicy
首先说明,无论CachePolicy被设置成什么,只要session的config中配置了NSURLCache对象,那么,这个session中的GET请求就会执行缓存操作,当然是否缓存成功还要看是否实现了willCacheResponse这个delegate 进行控制。
所以在IOS 这套网络框架里,CachePolicy不是决定是否缓存请求,而是决定,如何使用缓存
在NSURLSessionConfiguration里面可以配置一个缓存策略
1 | NSURLSessionConfiguration *config=[NSURLSessionConfiguration defaultSessionConfiguration]; |
在请求的时候,每个NSURLRequest也可以单独配置一个缓存策略
1 | NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.xxx.com"]]; |
经过测试,当NSURLRequest没有配置缓存策略的时候,会使用session的config配置的缓存策略,
如果NSURLRequest配置了缓存策略,那么使用NSURLRequest自己的缓存策略
NSURLRequestUseProtocolCachePolicy
使用HTTP协议定义的缓存字段来控制缓存行为,只支持Cache-control: max-age=xxx这种形式,前面提到,如果配置了NSURLCache,每个GET请求都会被缓存,当相同的请求第二次请求的时候,查看缓存的响应头中是否包含Cache-control: max-age=xxx
如果不包含,那么根据HTTP协议的定义,这个请求是不需要被缓存的,所以,即使本地已经有缓存,也不会使用,而是重新发起网络请求
如果包含Cache-control: max-age=xxx查看缓存是否已经失效,如果没失效,返回缓存数据,不发起请求,如果失效,那么重新发起网络请求
NSURLRequestReloadIgnoringLocalCacheData|NSURLRequestReloadIgnoringCacheData
这两个策略的定义是一样的,都是忽略本地缓存,每次都发起网络请求
NSURLRequestReturnCacheDataElseLoad
如果本地有缓存,使用缓存,不需要去判断Cache-control: max-age=xxx这些东西,如果没有缓存,发起网络请求
NSURLRequestReturnCacheDataDontLoad
如果本地有缓存,使用缓存,不需要去判断Cache-control: max-age=xxx这些东西,如果没有缓存,返回失败,不进行网络请求,可以理解为离线模式
Last-Modified/ETag
上面提到这么多缓存策略,其实与HTTP协议缓存相关的只有NSURLRequestUseProtocolCachePolicy这种缓存策略,只有在这种策略下,才会用到Cache-control这些东西,并且只支持Cache-control这种缓存逻辑,那么我们之前文章提到过的Last-Modified,Etag这种缓存逻辑要怎么实现呢。
Last-Modified,Etag这两种缓存逻辑,没有被IOS封装进网络框架,所以如果我们想要支持这两种缓存逻辑,需要我们自己来实现。
下面用Last-Modify的方式举一个例子
1 | NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://wthrcdn.etouch.cn/weather_mini?city=北京"]]; |