為什麼要理解NSURLCache?
在iOS app的開發過程中,網絡數據的緩存一直來說都是一件常見,但又繁瑣的任務,經常會有各種老大們提出這樣那樣的緩存要求。
一般而言緩存技術都無外乎以下幾點。第一種則是使用系統內置的緩存處理機制,就如本文所提到的,包括了使用一些第三方庫,也是使用系統底層內置的緩存。另外一種則是使用額外的緩存工具,比如數據庫、文件存儲等進行保存,同時自己來控制緩存的策略。
緩存的方式很多,無論你選擇使用哪一種,我相信理解這些系統底層的處理機制對你最終選擇自己的處理策略會很有幫助。
NSURLCache
NSURLCache是iOS系統用於實現網絡緩存的一個組件,被放於NSURL Loading這一個功能組件中。
在NSURLConnection加載系統中,緩存被設計為request對象的一個屬性,由NSURLRequest對象的cachePolicy屬性指定。而在NSURLSession加載系統中,緩存被設計為NSURLSessionConfiguration對像的一個屬性,該屬性所指定的策略被該session的所有request所共享。
可選的緩存策略
NSURLRequestUseProtocolCachePolicy
NSURLRequestUseProtocolCachePolicy是默認的緩存策略,它使用當前URL的協議中預置的緩存策略,無論這個協議是http,還是說你自己定義協議。
對於我們常見的http協議來說,這個策略根據請求的頭來執行緩存策略。服務器可以在返回的響應頭中加入Expires策略或者Cache-Control策略來告訴客戶端應該執行的緩存行為,同時配合#Last-Modified#等頭來控制刷新的時機。
有關具體的http緩存策略這裡不詳述。
NSURLRequestReloadIgnoringCacheData
NSURLRequestReloadIgnoringCacheData 這個策略則根本不會緩存數據
NSURLRequestReturnCacheDataElseLoad
NSURLRequestReturnCacheDataElseLoad 這個策略比較有趣,它會一直償試讀取緩存數據,直到無法沒有緩存數據的時候,才會去請求網絡。這個策略有一個重大的缺陷導致它根本無法被使用,即它根本沒有對緩存的刷新時機進行控制,如果你要去使用它,那麼需要額外的進行對緩存過期進行控制。
NSURLRequestReturnCacheDataDontLoad
這個選項只讀緩存,無論何時都不會進行網絡請求。
精確的緩存控制
在默認的這幾個選項之下,如果你需要對緩存進行精確的控制或者修改,則需要實現NSURLProtocol子類了
NSURLSession的數據和上傳任務,可以實現URLSession:dataTask:willCacheResponse:completionHandler:方法。另外,下載的任務在這裡是不可用的。
對於NSURLConnection,則實現connection:willCacheResponse:方法
NSURLSession提供一個block來告之會話需要緩存什麼東西,而NSURLConnection代理則需要返回一個NSURLCachedResponse對象。
一般來說,如果你需要修改需要緩存的內容,那麼你需要新創建一個NSURLCachedResponse對象來被緩存,同時用於下一次的返回。另外返回nil則會阻止緩存行為。如:
\- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { NSMutableDictionary *mutableUserInfo = [[cachedResponse userInfo] mutableCopy]; NSMutableData *mutableData = [[cachedResponse data] mutableCopy]; NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly; // ... return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response] data:mutableData userInfo:mutableUserInfo storagePolicy:storagePolicy]; }
或者
\- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { return nil; }
與HTTP服務器進行交互的簡單說明
Cache-Control頭
在第一次請求到服務器資源的時候,服務器需要使用Cache-Control這個響應頭來指定緩存策略,它的格式如下:Cache-Control:max-age=xxxx,這個頭指指明緩存過期的時間
Cache-Control頭具有如下選項:
public: 指示可被任何區緩存
private
no-cache: 指定該響應消息不能被緩存
no-store: 指定不應該緩存
max-age: 指定過期時間
min-fresh:
max-stable:
Last-Modified/If-Modified-Since
Last-Modified 是由服務器返回響應頭,標識資源的最後修改時間.
If-Modified-Since 則由客戶端發送,標識客戶端所記錄的,資源的最後修改時間。服務器接收到帶有該請求頭的請求時,會使用該時間與資源的最後修改時間進行對比,如果發現資源未被修改過,則直接返回HTTP 304而不返回包體,告訴客戶端直接使用本地的緩存。否則響應完整的消息內容。
Etag/If-None-Match
Etag 由服務器發送,告之當資源在服務器上的一個唯一標識符。
客戶端請求時,如果發現資源過期(使用Cache-Control的max-age),發現資源具有Etag聲明,這時請求服務器時則帶上If-None-Match頭,服務器收到後則與資源的標識進行對比,決定返回200或者304。