iOS网络请求缓存:NSURLCache详解

我读过一些开源项目的网络请求缓存的代码,基本上都是采用在本地存文件的方式进行缓存。若是你打算在你的项目中加入网络请求的缓存,可能你并不须要本身造一个轮子,了解一下NSURLCache就足够。html

这是一个Apple已经为你准备好了的网络请求缓存类。网上对这个类的介绍并很少,而且有的文章讲得很不详细。但愿这篇文章能让你对NSURLCache有一个比较详细的了解。git

缓存

首先,NSURLCache提供的是内存以及磁盘的综合缓存机制。许多文章谈到,使用NSURLCache以前须要在AppDelegate中缓存空间的设置:github

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

然而若是你不添加上面的代码,而且运行以下代码,能够看到:数据库

print(NSURLCache.sharedURLCache().diskCapacity)
//output:
//10000000

print(NSURLCache.sharedURLCache().memoryCapacity)
//output:
//512000复制代码

也就是说,其实默认就已经设置好了512kb的内存缓存空间,以及10MB的磁盘缓存空间。可能你的代码中并无写任何与NSURLCache有关的东西,但其实它已经默默的开始帮你进行缓存了。swift

已经缓存上了,可是怎么使用缓存呢?请继续往下。缓存

缓存策略

GET

不用多说,NSURLCache只会对你的GET请求进行缓存。服务器

NSURLRequestCachePolicy

NSURLRequest中有个属性:网络

public var cachePolicy: NSURLRequestCachePolicy { get }复制代码

你能够经过这个属性来设置请求的缓存策略,session

public enum NSURLRequestCachePolicy : UInt {

    case UseProtocolCachePolicy // 默认值

    case ReloadIgnoringLocalCacheData // 不使用缓存数据
    case ReloadIgnoringLocalAndRemoteCacheData // Unimplemented
    public static var ReloadIgnoringCacheData: NSURLRequestCachePolicy { get }

    case ReturnCacheDataElseLoad // 不管缓存是否过时都是用缓存,没有缓存就进行网络请求
    case ReturnCacheDataDontLoad // 不管缓存是否过时都是用缓存,没有缓存也不会进行网络请求

    case ReloadRevalidatingCacheData // Unimplemented
}复制代码

其实其余几个值都比较好理解,惟独默认值UseProtocolCachePolicy让我不太懂。app

字面上的意思是按照协议的缓存策略进行缓存,那么这是什么协议呢?http协议

详细:RFC 2616, Section 13

服务器返回的响应头中会有这样的字段:Cache-Control: max-age or Cache-Control: s- maxage,经过Cache-Control来指定缓存策略,max-age来表示过时时间。根据这些字段缓存机制再采用以下策略:

  • 若是本地没有缓存数据,则进行网络请求。
  • 若是本地有缓存,而且缓存没有失效,则使用缓存。
  • 若是缓存已经失效,则询问服务器数据是否改变,若是没改变,依然使用缓存,若是改变了则请求新数据。
  • 若是没有指定是否失效,那么系统将本身判断缓存是否失效。(一般认为是6-24小时的有效时间)

其实我之前对Cache-Control之类的也并不太了解 T_T,本身默默的print了一下响应头,你能够看到:

print((response as? NSHTTPURLResponse)?.allHeaderFields)

//响应头中:Cache-Control: no-cache复制代码

这也就是为何,虽然NSURLCache一直在默默的缓存,可是我并无感觉到,固然或许你那里不同。这个no-cache就表示不缓存。(勘误) 修正:no-cache表示不使用缓存,可是会缓存,no-store表示是不进行缓存。

这里要额外提一句,看到网上有同窗说本身出现了某个请求数据一直使用缓存,没有被更新。这种状况可能就是服务器返回的Cache-Control有误。

打开沙盒路径下的Library/Caches 中,你能够看到缓存文件:

沙盒中的缓存文件

这能够说明存在磁盘上的数据是存在数据库里的,性能不用担忧。打开数据库文件就能够看到请求的数据。

缓存数据

cfurl_cache_response表中能够看到有一个字段是request_key,经过里面的值能够推断每个response是经过请求的url+参数来做为key储存的。

固然,通过个人屡次试验,在Cache-Control: no-cache的状况下,NSURLCache也会进行缓存,可是并不使用缓存数据。

总结一下:默认状况下NSURLCache的缓存策略是根据http协议来的,服务器经过Cache-Control: max-age字段来告诉NSURLCache是否须要缓存数据。

缓存封装

若是你不打算采用http协议的缓存策略,依然能够使用NSURLCache进行缓存。

public func cachedResponseForRequest(request: NSURLRequest) -> NSCachedURLResponse?复制代码

你能够经过这个方法,传入请求,来获取缓存。NSCachedURLResponse保存了上次请求的数据以及响应头。

public func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest request: NSURLRequest)复制代码

NSURLSessionDelegate协议中有以下方法,能够对即将缓存的数据进行修改,添加userInfo,在代理方法中必须调用completionHandler,传入将要缓存的数据,若是传nil则表示不缓存。

optional public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: (NSCachedURLResponse?) -> Void)复制代码

Alamofire中能够这样写:

Alamofire.Manager
.sharedInstance
.delegate
.dataTaskWillCacheResponse = { (session, task, cachedResponse) -> NSCachedURLResponse? in
    var userInfo = [NSObject : AnyObject]()
    // 设置userInfo
    return NSCachedURLResponse(response: cachedResponse.response,
                               data: cachedResponse.data,
                               userInfo: userInfo,
                               storagePolicy: cachedResponse.storagePolicy)
}复制代码

#参考

个人blog:Roxily's Blog

简书:Roxily

我也只是简单的对NSURLCache进行了介绍,须要深刻了解的话你们仍是须要拜读一下文章,但愿能给你们一些帮助:

相关文章
相关标签/搜索