关于UIWebview缓存策略

UIWebview缓存策略

说到UIWebview的缓存问题,就不得不提 NSURLRequest 的缓存策略 cachePolicycachePolicy枚举如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    NSURLRequestUseProtocolCachePolicy = 0,
    NSURLRequestReloadIgnoringLocalCacheData = 1,
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
    NSURLRequestReturnCacheDataElseLoad = 2,
    NSURLRequestReturnCacheDataDontLoad = 3,
    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};

枚举中我们真正能使用的枚举只有NSURLRequestUseProtocolCachePolicyNSURLRequestReloadIgnoringLocalCacheDataNSURLRequestReturnCacheDataElseLoadNSURLRequestReturnCacheDataDontLoad 这四种类型。

各个缓存策略理解

NSURLRequestReloadIgnoringLocalCacheData

不理会本地的缓存是否有效或者需要更新,直接从源代码处拉取最新数据。

NSURLRequestReturnCacheDataElseLoad

如果本地已有缓存(不理会本地的缓存是否有效或者需要更新),则直接返回;否则从源代码处拉取最新的数据。

NSURLRequestReturnCacheDataElseLoad

如果本地已有缓存(不理会本地的缓存是否有效或者需要更新),则直接返回;否则被认为请求失败,直接返回错误。
这种缓存策略,类似于“脱机”模式。

NSURLRequestUseProtocolCachePolicy

默认的缓存策略,这种缓存策略会根据request和response中的headfields中的相关头部信息中获取对应的缓存方案。
很多人对这个默认缓存策略摸不着头脑,不知道它到底是怎么运作的。只是感觉它一会读缓存,一会不读缓存,一会缓存很久(杀了App都还有,甚至连404这样的错误都会被缓存起来)。之所以会出现这些现象是因为这个缓存策略会根据http headfields中的相关字段来控制缓存,比如缓存新鲜度时长、缓存是否需要刷新等。headfields中控制于缓存相关的字段有 Expires,Cache-Control,Last-Modified,ETag。下面就介绍下这些字段。

1、Expires

(过期时间)HTTP头信息Expires(过期时间) 属性是HTTP控制缓存的基本手段,这个属性告诉缓存器:相关副本在多长时间内是新鲜的。过了这个时间,缓存器就会向源服务器发送请求,检查文档是否被修改。几乎所有的缓存服务器都支持Expires(过期时间)属性;
格式:

1
"Expires" ":" HTTP-date

例如

1
Expires: Thu, 01 Dec 1994 16:00:00 GMT (必须是GMT格式)

2、Cache-Control

Cache-control用于控制HTTP缓存(在HTTP/1.0中可能部分没实现,仅仅实现了Pragma: no-cache)
数据包中的格式:

1
Cache-Control: 'cache-directive'

‘cache-directive’可以为以下参数:

  • request时可以使用以下参数:
1
2
3
4
5
6
7
8
| "no-cache"
| "no-store"
| "max-age" "=" delta-seconds
| "max-stale" [ "=" delta-seconds ]
| "min-fresh" "=" delta-seconds
| "no-transform"
| "only-if-cached"
| "cache-extension"
  • response时可以使用以下参数:
1
2
3
4
5
6
7
8
9
10
| "public"
| "private" [ "=" <"> field-name <"> ]
| "no-cache" [ "=" <"> field-name <"> ]
| "no-store"
| "no-transform"
| "must-revalidate"
| "proxy-revalidate"
| "max-age" "=" delta-seconds
| "s-maxage" "=" delta-seconds
| "cache-extension"

部分说明:

  • 根据是否可缓存分为
    Public 指示响应可被任何缓存区缓存。
    Private 指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
    no-cache 指示请求或响应消息不能缓存(HTTP/1.0用Pragma的no-cache替换)

  • 根据什么能被缓存
    no-store 用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。

  • 根据缓存超时
    max-age 指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
    min-fresh 指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
    max-stale 指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

3、Last-Modified/If-Modified-Since

Last-Modified/If-Modified-Since用于本地缓存更新检测。
客户端发送请求时,request 的headFields中加入If-Modified-Since,表示上一次获取的修改日期。
服务端接收到请求后,根据If-Modified-Since判断数据是否有变化。如果没有变化,则直接返回304;如果发生了变化,则返回最新数据,并在response的headfields中加入Last-Modified,表示最新的修改时间。

4、ETag/If-None-Match

ETag 是实现与最近修改数据检查同样的功能的另一种方法:没有变化时不重新下载数据。其工作方式是:服务器发送你所请求的数据的同时,发送某种数据的 hash (在 ETag 头信息中给出)。hash 的确定完全取决于服务器。当第二次请求相同的数据时,你需要在 If-None-Match:头信息中包含 ETag hash,如果数据没有改变,服务器将返回 304 状态代码。

这几个http头可以作为meta标签发送到客户端,但是需要注意的是Http头中的设置优先级更高一些,例如:

1
2
<meta. http-equiv=”Expires” CONTENT=” Fri, 30 Oct 1998 14:19:41″>
<meta. http-equiv=”Cache-Control” CONTENT=”no-cache”>

总结:

Expires/Cache-Control Header是控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只是Cache-ControlExpires可以控制的多一些, 而且Cache-Control会重写Expires的规则。

Last-Modified/If-Modified-SinceETag/If-None-Match是浏览器发送请求到服务器后判断文件是否已经修改过,如果没有修改过就只发送一个304回给浏览器,告诉浏览器直接从自己本地的缓存取数据;如果修改过那就整个数据重新发给浏览器。

了解了这些字段后,我们就可以抓取下http包来分析下为什么页面不刷新,什么时候页面会刷新了.

本文作者: ctinusdev
本文链接: https://ctinusdev.github.io/2016/03/03/UIWebViewCache/
转载请注明出处!

坚持原创技术分享,您的支持将鼓励我继续创作!