背景
最近關于web界面偶有反饋拉到舊的界面,導致出現一些異常情況;
因此,對web資源的加載、緩存進行一些梳理。
正文
一、緩存相關概念介紹
NSURLCache是iOS系統常用的web緩存方式,通過
[NSURLCache sharedURLCache]
獲取默認的緩存相關信息;可以在啟動的時候,通過[NSURLCache setSharedURLCache:URLCache]
的方式設置一個自定義的NSURLCache。NSCache和NSURLCache名字相近,其實沒有什么關系;NSCache可以認為是一個字典緩存,在內存不足的時候會自動釋放對象。雖然是系統提供的官方緩存類,但是實際開發中并沒有使用,替代者是YYCache。
URLProtocol是iOS系統對URL請求行為進行抽象,細化出每一步操作,讓開發者可以針對每一步進行代理,實現對特定請求的攔截,并返回本地的數據。
使用的時候,首先通過canInitWithRequest:(NSURLRequest *)request
,告訴系統要進行代理;
然后在startLoading
中,通過判斷request和本地緩存信息,判斷本次請求是否可以返回本地數據,并相應調用client的方法;
舉例,下面就是讀取本地數據,判斷ETag是否相同,進而返回304的邏輯:
NSString *requestETag = request.allHTTPHeaderFields[@"If-None-Match"];
NSString *etag = localData.eTag?:@"";
NSDictionary *headerFields = @{@"Cache-Control" : @"max-age=600", @"ETag":etag, @"Access-Control-Allow-Origin" : @"*"};
if (requestETag.length > 0 && [requestETag isEqualToString:etag]) {
NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:304 HTTPVersion:nil headerFields:headerFields];
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
[self.client URLProtocolDidFinishLoading:self];
}
NSURLCache和URLProtocol的差別:
1、NSURLCache只支持GET請求,URLProtocol還支持Post請求;
2、NSURLCache清理緩存通常使用removeAllCachedResponses清理全部緩存,URLProtocol是代理資源加載過程,本地磁盤的資源存儲由業務控制;
二、HTTP的緩存機制
以某個web界面加載為例,當我們不使用瀏覽器緩存時,返回的response是完整的html文本,同時還附帶著ETag;
如果打開緩存策略,則請求頭帶了If-None-Match(對應直接的ETag: "5e58f3dd-b0b"),此時回包體積明顯變小,同時返回碼是304;
當請求或者response帶有no-cache、max-age=0時,緩存的資源仍可使用,但是會通過請求進行驗證,類似上面的ETag,返回304表示Not Modified,可以繼續使用;(no-cache,并非放棄緩存)
而當max-age=3600時,表示資源有效時間是1個小時,在有效時間內不需要通過后端驗證,此時不需要發起網絡請求,會直接由cache返回數據。(前提是客戶端的request的header,沒有設置no-cache和max-age=0)
一個資源的請求流程:
關于request和response的總結:
- request的header是資源請求的核心控制參數,如果request的cache策略是no-cache或者max-age=0,則一定會驗證資源;
- request沒有設置cache-control的策略,則按照response的策略進行,如果age大于reponse的max-age或者response設置了no-cache,則會進行資源校驗;如果reponse設置了max-age=x,客戶端的age當前小于x,則不會發起網絡請求,直接使用cache的數據;
web同學表示,web界面通常不會設置request的cache-control,因為靜態資源的加載永遠在js之前;
即使是在html的最前面加上cache-control的<meta>標簽,也是在html拉到之后才能生效;
(但是客戶端開發可以設置request-header)
三、業務緩存邏輯(web緩存SDK)
在前面的client->cache->server基礎上,web緩存SDK所在的層級是在cache和server之間;
cache屬于瀏覽器自身的緩存,web緩存SDK相當于代理,阻斷了瀏覽器發起的網絡請求,如果本地有匹配的數據,則使用本地數據返回,如果沒有使用網絡請求,最終所有的數據都會加載到cache;
web緩存SDK和上面的緩存策略并沒有關系,上面的緩存策略決定是否要發起網絡請求去驗證資源、加載資源,而web緩存SDK則是在請求發起之后直接返回,類似charles的map local;
四、一個歷史教訓
線上的web界面出現一個bug,web的同學修復完之后,手動刷新了cdn的資源和業務緩存SDK的資源。
但是部分html配置的no-cache失效(設置了max-age=xxx),導致如果之前進入過在拉到之前,會使用瀏覽器緩存;導致本次啟動會一直使用舊的的界面。
解決方案:
1、更換該界面的url,使得cache失效;
2、清除webKit的緩存;
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate dateWithTimeIntervalSince1970:0] completionHandler:^{
//清除靜態資源成功
}];
總結
HTTP協議的學問博大精深,這次借此對緩存相關知識進行一次梳理。
參考鏈接
https://stackoverflow.com/questions/27105094/how-to-remove-cache-in-wkwebview