我讀過一些開源專案的網路請求快取的程式碼,基本上都是採用在本地存檔案的方式進行快取。如果你打算在你的專案中加入網路請求的快取,可能你並不需要自己造一個輪子,瞭解一下NSURLCache
就足夠。
這是一個Apple已經為你準備好了的網路請求快取類。網上對這個類的介紹並不多,並且有的文章講得很不詳細。希望這篇文章能讓你對NSURLCache
有一個比較詳細的瞭解。
快取
首先,NSURLCache
提供的是記憶體以及磁碟的綜合快取機制。許多文章談到,使用NSURLCache
之前需要在AppDelegate
中快取空間的設定:
1 2 3 4 5 6 7 8 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil]; [NSURLCache setSharedURLCache:URLCache]; } |
然而如果你不新增上面的程式碼,並且執行如下程式碼,可以看到:
1 2 3 4 5 6 7 |
print(NSURLCache.sharedURLCache().diskCapacity) //output: //10000000 print(NSURLCache.sharedURLCache().memoryCapacity) //output: //512000 |
也就是說,其實預設就已經設定好了512kb的記憶體快取空間,以及10MB的磁碟快取空間。可能你的程式碼中並沒有寫任何與NSURLCache
有關的東西,但其實它已經默默的開始幫你進行快取了。
已經快取上了,但是怎麼使用快取呢?請繼續往下。
快取策略
GET
不用多說,NSURLCache
只會對你的GET
請求進行快取。
NSURLRequestCachePolicy
NSURLRequest
中有個屬性:
1 |
public var cachePolicy: NSURLRequestCachePolicy { get } |
你可以通過這個屬性來設定請求的快取策略,
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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了一下響應頭,你可以看到:
1 2 3 |
print((response as? NSHTTPURLResponse)?.allHeaderFields) //響應頭中:Cache-Control: no-cache |
這也就是為什麼,雖然NSURLCache
一直在默默的快取,但是我並沒有感受到,當然或許你那裡不一樣。這個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
進行快取。
1 |
public func cachedResponseForRequest(request: NSURLRequest) -> NSCachedURLResponse? |
你可以通過這個方法,傳入請求,來獲取快取。NSCachedURLResponse
儲存了上次請求的資料以及響應頭。
1 |
public func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest request: NSURLRequest) |
NSURLSessionDelegate
協議中有如下方法,可以對即將快取的資料進行修改,新增userInfo,在代理方法中必須呼叫completionHandler,傳入將要快取的資料,如果傳nil則表示不快取。
1 2 3 4 |
optional public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: (NSCachedURLResponse?) -> Void) |
在Alamofire
中可以這樣寫:
1 2 3 4 5 6 7 8 9 10 11 |
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
進行了介紹,需要深入瞭解的話大家還是需要拜讀一下文章,希望能給大家一些幫助: