NSURLCache

511 阅读5分钟

作者:Mattt

NSURLCache给你的程序的URl请求提供了内存和磁盘结合的缓存机制。Foundation的URL Loading System的一部分,任何通过NSURLConnection加载的请求都会由NSRULCache处理。

网络缓存减少了需要向服务器请求的次数,提高了离线或者低网络环境下使用程序的体验。

当一个请求完成了从服务器返回的响应时,一个缓存的响应会被保存到本地。相同的请求在下次产生时,本地缓存的响应会立即返回,不会连接服务器。NSURLCache会自动、明确的返回缓存响应。

自ios5开始,会为程序默认设置一个共享的NSURLCache。:

没有特殊缓存请求或者要求的程序需要查找默认共享的可接受的缓存。有多个特殊需求的程序可以创建一个NSURLCache对象并且使用setSharedURLCache:来将它设置为共享缓存实例。程序需要在任何调用这个方法的请求之前完成这个操作。

有这样特殊缓存要求的程序可以设置在IOS-application:didFinishLaunchingWithOptions:或者OS X的–applicationDidFinishLaunching:中设置一个共享URL缓存:

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
    let URLCache = NSURLCache(memoryCapacity: 4 * 1024 * 1024, diskCapacity: 20 * 1024 * 1024, diskPath: nil)
    NSURLCache.setSharedURLCache(URLCache)

    return true
}

缓存策略是在客户端请求和服务端应答中指定的。理解了这些策略和他们如何相互影响的是查找对你程序最佳表现得基本要求。

NSURLRequestCachePolicy

NSURLRequest有一个缓存策略属性,通过下面常量指定请求的缓存方式:

  • NSURLRequestUseProtocolCachePolicy:特殊的的URL加载请求使用定义在协议视线中的缓存逻辑。这是默认的策略。
  • NSURLRequestReloadIgnoringLocalCacheData:需要从远程端资源加载数据。不可以使用已存在的缓存数据。
  • NSURLRequestReloadIgnoringLcalAndRemoteCacheData:不只本地缓存数据要被忽略,而且在协议允许范围内应该指示代理和其他中间体忽略他们的缓存。
  • NSURLRequestReturnCacheDataElseLoad:存在的缓存数据应该使用,忽略他的市场或者过期时间。如果在缓存中没有对应于请求的已存在的数据,数据要从远程的资源中加载。
  • NSURLRequestReturnCacheDataDontLoad:存在的缓存数据应该使用,忽略他的市场或者过期时间。如果在缓存中没有对应于请求的已存在的数据,不会尝试从远程资源中加载数据,加载认为已经失败了,(例如离线模式)。
  • NSURLRequestReloadRevalidatingCacheData:如果远程资源确认其有效性,则可以使用现有的缓存数据,不然从远程资源中加载URL。

你可能不会感到惊喜,因为这些值晦涩难懂还会经常搞混了。

事实上还会更加迷惑,因为NSURLRequestReloadIgnoringLcalAndRemoteCacheData和NSURLRequestReloadRevalidatingCacheData还没有实现。

HTTP 缓存语义

因为NSURLConnection是为了支持多种协议而设计的-包括FTP和HTTP/HTTPS-加载系统API的URL以一种与协议无关的方式。因为这篇文章的目的,缓存会用HTTP语义的词组来解释。

HTTP请求和响应使用headers来交流信息,像字符编码,MIME类型,缓存指令。

请求缓存头

默认,NSURLRequest会使用当前时间来确定是否要返回一个缓存响应。为了更简明的缓存控制,可以使用下面的头:

  • If-Modified-Since -这个请求头对应LastModified响应头。将这个值设置为上一个Last-Modified值的Last-Modified值一样的结点。
  • If-None-Match -这个请求头对应于Etag响应头。使用之前接收到的Etag值作为上一个请求的结点。

响应缓存头

一个NSHTTPURLResponse包含了一组HTTP头,包括下面得响应如何缓存的指示:

  • Cache-Control -这个头必需在来自服务端的响应中来是客户端可以缓存HTTP。这个头的值可能包含像max-age(缓存一个响应要多长时间)的信息,响应是否用public或者private方式缓存,或者no-cache(不缓存)。

除了Cache-Control,服务器可能发送所需的有条件的请求的额外的头(像之前章节提到的):

  • Last-Modified -这个头的值对应于请求的资源最后一次改变的日期。例如,如果客户端请求最近照片的时间线,/photos/timeline,Last-Modified值可以设置为最近拍照的日期。
  • Etag-一个“个体标签”的缩写,这是一个表示请求资源的内容的标识。实际上,ETag头值可以是像资源属性的MD5摘要。这对于可能没有明确Last-Modified值的动态生成资源非常有用。

NSURLConectionDelegate

一旦接收到服务器响应,NSURLConnection代理有机会在-connection:willCacheResponse中指定缓存响应。

NSCachedURLResponse是一个类,包含了一个和响应相关的缓存数据的NSURLResponse。

在-connection:willCacheResponse:中,cachedResponse对象从URL链接的结果中自动创建。因为没有对应于NSCachedUrlResponse的可变类型,为了改变cachedResponse,必须要构建一个新的对象,将修改的值吵到-initWithResonse:data:useInfo:storagePolicy:,例如:

// MARK: NSURLConnectionDataDelegate

func connection(connection: NSURLConnection!, willCacheResponse cachedResponse: NSCachedURLResponse!) -> NSCachedURLResponse! {
    var mutableUserInfo = NSMutableDictionary(dictionary: cachedResponse.userInfo)
    var mutableData = NSMutableData(data: cachedResponse.data)
    var storagePolicy: NSURLCacheStoragePolicy = .AllowedInMemoryOnlyreturn NSCachedURLResponse(response: cachedResponse.response, data: mutableData, userInfo: mutableUserInfo, storagePolicy: storagePolicy)
}

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

如果没有实现,NSURLCOnnection就会使用传递到-conneciton:willCacheResponse:中的缓存响应,所以除非你需要改变或者避免霍村,这个方法不需要在代理中实现。

Caveats

NSURLCache就像同名同辈NSCache一样,也有一些独有的特点。

自IOS5开始,支持磁盘缓存,但是只对于HTTP,不是HTTPS,请求(IOS6增加了这个支持)。。