参考:https://github.com/ChenYilong/ParseSourceCodeStudy/blob/master/02_Parse的网络缓存与离线存储/iOS网络缓存扫盲篇.md
GET网络请求缓存
首先要知道,POST请求不能被缓存,只有 GET 请求能被缓存。因为从数学的角度来讲,GET 的结果是
幂等
的,就好像字典里的 key 与 value 就是
幂等
的,而 POST 不
幂等
。缓存的思路就是将查询的参数组成的值作为 key ,对应结果作为value。从这个意义上说,一个文件的资源链接,也叫 GET 请求,下文也会这样看待。
80%的缓存需求:两行代码就可满足
设置缓存只需要三个步骤:
第一个步骤:请使用 GET 请求。
第二个步骤:
如果你已经使用 了 GET 请求,iOS 系统 SDK 已经帮你做好了缓存。你需要的仅仅是设置下内存缓存大小、磁盘缓存大小、以及缓存路径。甚至这两行代码不设置也是可以的,会有一个默认值。代码如下:
NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:urlCache];
第三个步骤:没有第三步!
你只要设置了这两行代码,基本就可满足80%的缓存需求。AFNetworking 的作者 Mattt曾经说过:
无数开发者尝试自己做一个简陋而脆弱的系统来实现网络缓存的功能,殊不知 NSURLCache
只要两行代码就能搞定且好上 100 倍。
(AFN 是不是在暗讽 SDWebImage 复杂又蹩脚的缓存机制??)
要注意
- iOS 5.0开始,支持磁盘缓存,但仅支持 HTTP
- iOS 6.0开始,支持 HTTPS 缓存
控制缓存的有效性
我们知道:
- 只要是缓存,总会过期。
那么缓存的过期时间如何控制?
上文中的两行代码,已经给出了一个方法,指定超时时间。但这并也许不能满足我们的需求,如果我们对数据的一致性,时效性要求很高,即使1秒钟后数据更改了,客户端也必须展示更改后的数据。这种情况如何处理?
下面我们将对这种需求,进行解决方案的介绍。顺序是这样的:先从文件类型的缓存入手,引入两个概念。然后再谈下,一般数据类型比如 JSON 返回值的缓存处理。
文件缓存:借助ETag或Last-Modified判断文件缓存是否有效
Last-Modified
服务器的文件存贮,大多采用资源变动后就重新生成一个链接的做法。而且如果你的文件存储采用的是第三方的服务,比如七牛、青云等服务,则一定是如此。
这种做法虽然是推荐做法,但同时也不排除不同文件使用同一个链接。那么如果服务端的file更改了,本地已经有了缓存。如何更新缓存?
这种情况下需要借助
ETag
或
Last-Modified
判断图片缓存是否有效。
Last-Modified
顾名思义,是资源最后修改的时间戳,往往与缓存时间进行对比来判断缓存是否过期。
在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修改的时间,格式类似这样:
Last-Modified: Fri, 12 May 2006 18:53:33 GMT
客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过:
If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
总结下来它的结构如下:
请求 HeaderValue | 响应 HeaderValue |
---|---|
Last-Modified | If-Modified-Since |
如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
判断方法用伪代码表示:
if ETagFromServer != ETagOnClient || LastModifiedFromServer != LastModifiedOnClient
GetFromServer
else
GetFromCache
之所以使用
LastModifiedFromServer != LastModifiedOnClient
而非使用:
LastModifiedFromServer > LastModifiedOnClient
原因是考虑到可能出现类似下面的情况:服务端可能对资源文件,废除其新版,回滚启用旧版本,此时的情况是:
LastModifiedFromServer <= LastModifiedOnClient
但我们依然要更新本地缓存。
。。。。。。。。。