NSURLCache

原文地址:http://nshipster.com/nsurlcache/
如原作者发现有侵权行为可责令我在24小时之内删除,前提是你能看到。

NSURLCache 在你进行URL请求时为你的应用提供了一套内存和磁盘双重缓存机制。作为Foundation框架的URL Loading System的一部分,NSURLCache会处理所有通过NSURLConncetion(废弃了哇)载入的请求。

网络缓存减少了请求服务器的次数并且提高了在没有网络或者网络状况不好时候的用户体验。

当一个请求从服务器得到响应时,本地将会生成这个响应的一个缓存。下次有同样的请求发起式,本地缓存的响应将立刻返回,NSURLCache自动地返回缓存的响应。

从iOS5开始,应用程序默认有一个全局共享的NSURLCache引用文档

Applications that do not have special caching requirements or constraints should find the default shared cache instance acceptable. An application with more specific needs can create a custom NSURLCache object and set it as the shared cache instance using setSharedURLCache:. The application should do so before any calls to this method.

对于那些有特殊缓存需要的用户来说-可以在iOS平台下的application:didFinishLaunchingWithOptions:方法中或者Mac OS平台下的 –applicationDidFinishLaunching: 方法中对这个全局共享的URLcache进行设置:

- (BOOL)application:(UIApplication *)application 
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ 
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 
                                                     diskCapacity:20 * 1024 * 1024               
                                                         diskPath:nil]; 
[NSURLCache setSharedURLCache:URLCache];
}

对于客户端的请求和服务端的响应来说每一种缓存策略都是有特定用途的。理解这些缓存策略和每种策略之间的关联能够让你为自己的应用程序找到最佳的网络缓存解决方案。

NSURLRequestCachePolicy###

NSURLRequest有一个cachePolicy的属性,它根据下面这些常量来指定客户端请求缓存的方式:

  • NSURLRequestUseProtocolCachePolicy: 默认的缓存策略, 如果缓存不存在,直接从服务端获取。如果缓存存在,会根据response中的Cache-Control字段判断下一步操作,如: Cache-Control字段为must-revalidata, 则询问服务端该数据是否有更新,无更新的话直接返回给用户缓存数据,若已更新,则请求服务端。
  • NSURLRequestReloadIgnoringLocalCacheData:忽略本地缓存数据,直接请求服务端。
  • NSURLRequestReloadIgnoringLocalAndRemoteCacheData:忽略本地缓存,代理服务器以及其他中介,直接请求源服务端。
  • NSURLRequestReturnCacheDataElseLoad: 有缓存就使用,不管其有效性(即忽略Cache-Control字段), 无则请求服务端。
  • NSURLRequestReturnCacheDataDontLoad:总是加载本地缓存,不管其有效性,没有就失败, (确定当前无网络时使用)。
  • NSURLRequestReloadRevalidatingCacheData:缓存数据必须得得到服务端确认有效才使用(貌似是NSURLRequestUseProtocolCachePolicy中的一种情况)。

当你看到这些很难理解并经常搞混的枚举时并不会感到惊讶。

让人产生迷惑的其实是 NSURLRequestReloadIgnoringLocalAndRemoteCacheData
NSURLRequestReloadRevalidatingCacheDataaren’t even implemented! (Link to Radar)。

所以这才是你真正要知道的NSURLRequestCachePolicy:

Constant Meaning
UseProtocolCachePolicy 默认的缓存策略
ReloadIgnoringLocalCacheData 忽略本地缓存
ReloadIgnoringLocalAndRemoteCacheData 认真的说,不使用缓存
ReturnCacheDataElseLoad 有缓存就使用,不管其有效性, 无则请求服务端
ReturnCacheDataDontLoad 无网络时使用:总是加载本地缓存,不管其有效性,没有就失败
ReloadRevalidatingCacheData 缓存数据必须得得到服务端确认有效才使用

HTTP Cache Semantics


因为NSURLConnection支持多种协议,包括FTP和HTTP/HTTPS,URL Loading System的API指定的是一套与协议无关的缓存机制,本文中的缓存特指HTTP缓存。

HTTP请求和相应使用头文件传递数据源,包括编码方式,数据的接受类型和缓存指令

Request Cache Headers

NSURLRequest默认会根据当前时间判断是否需要返回一个已经缓存的响应。为了进行更精确的缓存控制,可以设置如下的Request Cache Headers

  • If-Modified-Since - 这个请求头字段和响应头中的Last-Modified字段相对应。Last-ModifiedIf-Modified-Since都是用来记录页面的最后修改时间。当客户端访问页面时,服务器会将页面最后修改时间通过 Last-Modified 标识由服务器发往客户端,客户端记录修改时间,再次请求本地存在的cache页面时,客户端会通过 If-Modified-Since 头将先前服务器端发过来的最后修改时间戳发送回去,服务器端通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回新的内容,如果是最新的,则 返回 304 告诉客户端其本地 cache 的页面是最新的,于是客户端就可以直接从本地加载页面了

  • If-None-Match -这个请求头字段和响应头中的Etag字段中相对应。客户端请求一个页面(A),服务器返回页面A,并在给A加上一个ETag, 客户端展现该页面,并将页面连同ETag一起缓存, 客户再次请求页面A,并将上次请求时服务器返回的ETag一起传递给服务器, 服务器检查该ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。

Response Cache Headers

一个NSHTTPURLResponse 包含了一套 HTTP 头, 通过下列的指令来告诉应用响应如何缓存:

  • Cache-Control - 这个字段必须包含在服务器发给具备HTTP缓存能力的客户端响应中头。它的值包含了许多信息,例如 max-age(缓存的有效期),public or private access决定所有内容都将被缓存还是内容只缓存到私有缓存中, no-cache根据 ETag,判断服务器端资源是否被更改,如果资源未被更改,可以避免下载。详情参见 Cache-Control section of RFC 2616

除了Cache-Control之外
, 根据请求头中特定的信息服务器的请求头中会包含与之相关的信息(例如上文中提到的几个字段):

  • Last-Modified -(不了解HTTP,这段我直接百度百科的..)在浏览器第一次请求某一个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
    如果服务器端的资源没有变化,则自动返回 HTTP 304(Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

  • Etag -是“entity tag”的简写,Etag值就类似于对资源文件进行的
    MD5
    加密后的标记,用来动态监测资源是否被修改。(具体流程不了解,继续百度百科..)
    ====第一次请求===
    1.客户端发起 HTTP GET 请求一个文件;
    2.服务器处理请求,返回文件内容和一堆Header,当然包括Etag(例如"2e681a-6-5d044840")(假设服务器支持Etag生成和已经开启了Etag).状态码200
    ====第二次请求===
    1.客户端发起 HTTP GET 请求一个文件,注意这个时候客户端同时发送一个If-None-Match头,这个头的内容就是第一次请求时服务器返回的Etag:2e681a-6-5d044840
    2.服务器判断发送过来的Etag和计算出来的Etag匹配,因此If-None-Match为False,不返回200,返回304,客户端继续使用本地缓存。

NSURLConnectionDelegate

一旦客户端接收到服务器的响应,NSURLConnection
缓存数据的类。

-connection:willCacheResponse:方法中, cachedResponse对象会从URL connection返回的结果中自动创建。因为没有可变的NSCachedURLResponse版本,想改变任何有关cachedResponse的东西,必须创建一个新的对象,并通过–initWithResponse:data:userInfo:storagePolicy:将改变的值传进去。例如:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{ 
 NSMutableDictionary *mutableUserInfo = [[cachedResponse userInfo] mutableCopy]; 
 NSMutableData *mutableData = [[cachedResponse data] mutableCopy];   
 NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly; 

 // ...

 return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response] 
                                                 data:mutableData 
                                                 userInfo:mutableUserInfo 
                                                 storagePolicy:storagePolicy];
}

如果 -connection:willCacheResponse:方法返回nil,响应不会被缓存。

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{ 
      return nil;
}

当-connection:willCacheResponse:方法没有实现的时候,NSURLConnection将会使用默认的缓存响应,否则会使用这个方法提供的NSCachedURLResponse,所以,除非你想改变或阻止响应缓存操作,不需要实现此代理方法。(上面的方法找了半天在NSURLConnectionDataDelegate这个代理中而不是文中提到的_ NSURLConnectionDelegate_,难道是坑?)

Caveats(忠告)

NSCache
,和NSURLCache并没有什么关系但看起来很相似的同生词, NSURLCache也有很多特性。
iOS 5开始支持磁盘缓存,但是仅仅适用于HTTP,不包括HTTPS请求(不过iOS6起开始支持)。 在Peter Steinberger深入研究了 他的NSURLCache子类的内部实现后,他 在博客写了一篇干货十足的文章

另一篇是Daniel Pasco在Black Pixel发表的描述了在和服务器通信时不设置缓存头发生的一些意想不到的事。


NSURLCache提醒了我们熟悉和系统交流是多么的重要。 当进行iOS和Mac OS开发是, 最主要的部分是 URL Loading System

无数的开发者在用他们网络缓存功能脆弱的系统时都会表现出恼怒和尴尬,因为他们不知道在NSURLCache中写两行代码的效果比用别的方式写100行代码更好。甚至更多的开发人员还不知道网络缓存的好处,而且从去不尝试解决,使他们的应用程序向服务器发出了无数的的不必要的请求。(原来刀哥说的这句话在这里)

改变你看问题的方式,在-application:didFinishLaunchingWithOptions:方法中设置全局共享的NSURLCache来确保你的app构建在一个稳固的基础之上。

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

推荐阅读更多精彩内容