Example 项目地址: https://github.com/zColdWater/URLRequestCacheExample 下载Demo,结合Demo一起实践更容易理解。
一,准备工作
在开始之前,我们需要清楚下面的一些问题,才方便我们后面的讲解。
1. URLRequest 涉及的范围
我们一提到URLRequest
,我相信很多国内的开发者,首先就会联想到,HTTP
请求,然后木有别的了。 但是其实 URLRequest
是一个很大的概念,它不只服务于HTTP
协议,它还服务于 其他应用协议,比如File
协议,Data
协议,自定义协议等等。 要么苹果公司为什么不叫它HTTPURLRequest
呢? 问题就在于我们平时最常接触的就是HTTP
协议,用来请求服务端的数据用来展示。
2. URLRequest.CachePolicy 涉及的范围
通过上面的文章我们清楚了 URLRequest
服务很多协议,那么URLRequest.CachePolicy
的范围是什么呢,很明显,和URLRequest
一样,这个缓存策略也包含上面这些协议,当然 我也不清楚其他协议的缓存策略是什么样子的,比如File
协议,或则别的。 但是我很清楚,我们常用的HTTP
协议的缓存协议,这个后面再讲,这里清楚的是,这个缓存策略支持很多协议,我们的HTTP
协议有着自己的缓存策略。
3. HTTP协议的缓存策略
我之前转了一篇台湾作者关于HTTP
协议的缓存策略的文章,文章地址是: http://47.99.237.180:8080/articles/2019/11/18/1574050998351.html
那么HTTP
协议的缓存策略是什么呢?
Note: 首先我们需要清楚的是,
HTTP
协议的策略是需要客户端
和服务端
配合完成的。也就是如果这个策略想要完成,需要双方都有动作,并且客户端
需要完全配合才行。
我用最直白的话来概述这个原理:
-
首先
客户端
第一次访问服务器
某一个资源,并且服务器
和客户端
协商好,我们都用标准的HTTP
缓存协议。 -
因为是第一次,
客户端
通过URL
来查找,发现本地没有缓存,直接向服务器发起一个HTTP
协议的网络请求。客户端
的请求头,例如:GET /EC6/poster-share-invite.png HTTP/1.1 Host: fep-sit.nioint.com:5418 Accept: */* User-Agent: SessionDownload/1 CFNetwork/1107.1 Darwin/19.0.0 Accept-Language: en-us Accept-Encoding: gzip, deflate Connection: keep-alive
这其实就是一个普通的网络请求,请求头也是客户端默认的,没有特殊设置。
-
服务器
接到了某一个客户端
的发来的请求,然后做的直接就看一下这个请求头有没有提供HTTP
协议缓存的相关字段,然后根据HTTP
协议缓存规则,来判断是否返回状态码304
让客户端用缓存,还是返回状态码200
,让客户端使用服务器返回的新资源,服务器检查请求头的相关字段如下:If-None-Match
: W/“20f9a-16f5c76370d” 这个字段你可以理解为这个资源的唯一Hash值,有点像MD5或者SHA1等,反正就是一个唯一标识啦,资源如果有变动,它一定就会有变动,并且这个值是从上一次服务器
返回的响应头里面的Etag
字段取来的。因为我们客户端是第一次请求,所以没有从之前的服务器响应里面拿到这个值,所以请求头就没有这个字段。If-Modified-Since
:Tue, 31 Dec 2019 14:57:28 GMT,这个字段表示的是最后一次资源更改的时间,同If-None-Match
也是从上一次的服务器响应头中拿到,从Last-Modified
字段取的。因为第一次请求,所以没有获取到上一次响应头的字段,也就没有带上。
-
服务器开始根据
HTTP
协议规则进行检查,来决定是让客户端使用缓存还是使用服务器下发的资源。- 服务器的两种响应头:
- 状态码200(告诉客户端,不要使用缓存,用我给你的新资源)
HTTP/1.1 200 OK X-Powered-By: Express Accept-Ranges: bytes Cache-Control: public, max-age=0 Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT ETag: W/"20f9a-16f5c76370d" Content-Type: image/gif Content-Length: 135066 Date: Wed, 01 Jan 2020 01:56:35 GMT Proxy-Connection: keep-alive
- 状态码304(告诉客户端你用自己的缓存即可)
这里注意的是,在iOS当中,你不需要亲自处理
304
的情况,如果你使用了默认的缓存策略,也就是使用HTTP协议本身的缓存策略,系统的网络框架比如URLSession
或者URLConnection
会自动的将这个304
处理成200
,这样方便了开发者逻辑处理,开发者只需要知道 资源获取成功,就可以了。HTTP/1.1 304 Not Modified X-Powered-By: Express Accept-Ranges: bytes Cache-Control: public, max-age=0 Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT ETag: W/"20f9a-16f5c76370d" Date: Wed, 01 Jan 2020 01:59:25 GMT Proxy-Connection: keep-alive
- 状态码200(告诉客户端,不要使用缓存,用我给你的新资源)
- 服务器响应头里与HTTP协议缓存策略相关的字段:
Cache-Control
: max-age = X (max-age=x 是告诉客户端x秒之内不要再发起请求了,就用你的缓存就OK了,换句话说,如果服务器告诉客户端max-age=100,客户端在100s之内再去请求,是不会发起真正的网络请求的,客户端的网络层框架会自动返回状态码200,上一次的缓存数据)Last-Modified
: Tue, 31 Dec 2019 14:57:28 GMT(这个字段是告诉客户端,这个资源最后一次更新的时间,让客户端保存好,下一次请求的时候,在请求头里面带上这个值,请求头的那个字段就是If-Modified-Since
,这里的规则是这样的,如果请求头里的If-Modified-Since
时间点早于服务器的Last-Modified
时间点,服务器会返回200,让客户端需要更新最新资源,如果反过来,或者相同,服务器会下发304,让客户端使用缓存。)ETag
: W/“20f9a-16f5c76370d”(这个字段告诉客户端,这个值是这个资源的唯一id,如果服务器上面有新资源,我们会更新这个值,客户端要保存好,下次请求的时候带上,下次请求头里面这个If-None-Match
字段就是保存的上次响应头里面的Etag
字段。它的规则是,如果客户端请求头中的If-None-Match
值不与服务器里面的Etag
一致,就返回200,让客户端使用新资源,只有当相等的情况,会返回304,让客户端使用缓存。)
- 服务器的两种响应头:
-
服务器检查完请求头发现这个客户端没有带上资源缓存信息,那么服务器就认为客户端不想使用
HTTP
协议缓存策略,返回200,把资源也一同返回。HTTP/1.1 200 OK X-Powered-By: Express Accept-Ranges: bytes Cache-Control: public, max-age=0 Last-Modified: Tue, 31 Dec 2019 14:57:28 GMT ETag: W/"20f9a-16f5c76370d" Content-Type: image/gif Content-Length: 135066 Date: Wed, 01 Jan 2020 01:56:35 GMT Proxy-Connection: keep-alive
-
客户端第一次拿到资源后,先将服务器告诉自己的缓存信息,保存起来,
Cache-Control: public, max-age=0
读取一下max-age
,发现值等于0,这是告诉我应该每次都发真正的请求,然后再存一下Last-Modified
上次更新的时间,再存一下ETag
,这个告诉我们资源的唯一id。 <