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進行了介紹,須要深刻了解的話你們仍是須要拜讀一下文章,但願能給你們一些幫助:

相關文章
相關標籤/搜索