我讀過一些開源項目的網絡請求緩存的代碼,基本上都是采用在本地存文件的方式進行緩存。如果你打算在你的項目中加入網絡請求的緩存,可能你并不需要自己造一個輪子,了解一下NSURLCache
就足夠。
這是一個Apple已經為你準備好了的網絡請求緩存類。網上對這個類的介紹并不多,并且有的文章講得很不詳細。希望這篇文章能讓你對NSURLCache
有一個比較詳細的了解。
緩存
首先,NSURLCache
提供的是內存以及磁盤的綜合緩存機制。許多文章談到,使用NSURLCache
之前需要在AppDelegate
中緩存空間的設置:
- (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
有關的東西,但其實它已經默默的開始幫你進行緩存了。
已經緩存上了,但是怎么使用緩存呢?請繼續往下。
緩存策略
GET
不用多說,NSURLCache
只會對你的GET
請求進行緩存。
NSURLRequestCachePolicy
NSURLRequest
中有個屬性:
public var cachePolicy: NSURLRequestCachePolicy { get }
你可以通過這個屬性來設置請求的緩存策略,
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
讓我不太懂。
字面上的意思是按照協議的緩存策略進行緩存,那么這是什么協議呢?http協議。
服務器返回的響應頭中會有這樣的字段: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-store表示是不進行緩存。no-cache
就表示不緩存
這里要額外提一句,看到網上有同學說自己出現了某個請求數據一直使用緩存,沒有被更新。這種情況可能就是服務器返回的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)
}
參考
我也只是簡單的對NSURLCache
進行了介紹,需要深入了解的話大家還是需要拜讀一下文章,希望能給大家一些幫助: