NSURLCache

作者: tongxyj | 来源:发表于2016-02-21 13:33 被阅读715次

    原文地址: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构建在一个稳固的基础之上。

    相关文章

      网友评论

        本文标题:NSURLCache

        本文链接:https://www.haomeiwen.com/subject/nqgvkttx.html