【轉(zhuǎn)】iOS網(wǎng)絡(luò)緩存掃盲篇

原帖地址

目錄

GET網(wǎng)絡(luò)請(qǐng)求緩存

當(dāng)我們?cè)谡務(wù)摼彺娴臅r(shí)候碗旅,我們?cè)谡務(wù)撌裁矗?/a>

80%的緩存需求:兩行代碼就可滿足

控制緩存的有效性

文件緩存:借助ETag或Last-Modified判斷文件緩存是否有效

Last-Modified

ETag

總結(jié)

一般數(shù)據(jù)類型借助 Last-Modified 與 ETag 進(jìn)行緩存

剩下20%的網(wǎng)絡(luò)緩存需求--真的有NSURLCache 不能滿足的需求渡处?

由于微信、QQ祟辟、微博医瘫、這類的應(yīng)用使用緩存很“重”,使一般的用戶也對(duì)緩存也非常習(xí)慣旧困。緩存已然成為必備醇份。

緩存的目的的以空間換時(shí)間

這句話在動(dòng)輒就是 300M稼锅、600M 的大應(yīng)用上,得到了很好的詮釋僚纷。但能有緩存意識(shí)的公司矩距,還在少數(shù)。

只有你真正感受到痛的時(shí)候怖竭,你才會(huì)考慮使用緩存锥债。

這個(gè)痛可能是:

服務(wù)器壓力、客戶端網(wǎng)絡(luò)優(yōu)化痊臭、用戶體驗(yàn)等等哮肚。

當(dāng)我們?cè)谡務(wù)摼彺娴臅r(shí)候,我們?cè)谡務(wù)撌裁矗?/p>

我們今天將站在小白用戶的角度广匙,給緩存這個(gè)概念進(jìn)行重新的定義允趟。

緩存有不同的分類方法:

這里所指的緩存,是一個(gè)寬泛的概念艇潭。

我們這里主要按照功能進(jìn)行劃分:

第一種 第二種

目的 優(yōu)化型緩存 功能型緩存
具體描述 出于優(yōu)化考慮:服務(wù)器壓力拼窥、用戶體驗(yàn)、為用戶剩流量等等蹋凝。同時(shí)優(yōu)化型緩存也有內(nèi)存緩存和磁盤(pán)緩存之分鲁纠。 App離線也能查看,出于功能考慮鳍寂,屬于存儲(chǔ)范疇
常見(jiàn)概念 GET網(wǎng)絡(luò)請(qǐng)求緩存改含、WEB緩存 離線存儲(chǔ)
典型應(yīng)用 微信首頁(yè)的會(huì)話列表、微信頭像迄汛、朋友圈捍壤、網(wǎng)易新聞新聞列表、 微信聊天記錄鞍爱、
Parse對(duì)應(yīng)的類 PFCachedQueryController PFOfflineStore

重度使用緩存的App: 微信鹃觉、微博、網(wǎng)易新聞睹逃、攜程盗扇、去哪兒等等。

GET網(wǎng)絡(luò)請(qǐng)求緩存

概述

首先要知道沉填,POST請(qǐng)求不能被緩存疗隶,只有 GET 請(qǐng)求能被緩存。因?yàn)閺臄?shù)學(xué)的角度來(lái)講翼闹,GET 的結(jié)果是 冪等 的斑鼻,就好像字典里的 key 與 value 就是冪等的,而 POST 不 冪等 猎荠。緩存的思路就是將查詢的參數(shù)組成的值作為 key 坚弱,對(duì)應(yīng)結(jié)果作為value蜀备。從這個(gè)意義上說(shuō),一個(gè)文件的資源鏈接史汗,也叫 GET 請(qǐng)求琼掠,下文也會(huì)這樣看待。

80%的緩存需求:兩行代碼就可滿足

設(shè)置緩存只需要三個(gè)步驟:

第一個(gè)步驟:請(qǐng)使用 GET 請(qǐng)求停撞。

第二個(gè)步驟:如果你已經(jīng)使用 了 GET 請(qǐng)求,iOS 系統(tǒng) SDK 已經(jīng)幫你做好了緩存悼瓮。你需要的僅僅是設(shè)置下內(nèi)存緩存大小戈毒、磁盤(pán)緩存大小、以及緩存路徑横堡。甚至這兩行代碼不設(shè)置也是可以的埋市,會(huì)有一個(gè)默認(rèn)值。代碼如下:

NSURLCache *urlCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];

[NSURLCache setSharedURLCache:urlCache];

第三個(gè)步驟:沒(méi)有第三步命贴!

你只要設(shè)置了這兩行代碼道宅,基本就可滿足80%的緩存需求。AFNetworking 的作者 Mattt曾經(jīng)說(shuō)過(guò):

無(wú)數(shù)開(kāi)發(fā)者嘗試自己做一個(gè)簡(jiǎn)陋而脆弱的系統(tǒng)來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)緩存的功能胸蛛,殊不知 NSURLCache 只要兩行代碼就能搞定且好上 100 倍污茵。

(AFN 是不是在暗諷 SDWebImage 復(fù)雜又蹩腳的緩存機(jī)制?葬项?)

要注意

iOS 5.0開(kāi)始泞当,支持磁盤(pán)緩存,但僅支持 HTTP

iOS 6.0開(kāi)始民珍,支持 HTTPS 緩存

控制緩存的有效性

我們知道:只要是緩存襟士,總會(huì)過(guò)期。

那么緩存的過(guò)期時(shí)間如何控制嚷量?

上文中的兩行代碼陋桂,已經(jīng)給出了一個(gè)方法,指定超時(shí)時(shí)間蝶溶。但這并也許不能滿足我們的需求嗜历,如果我們對(duì)數(shù)據(jù)的一致性,時(shí)效性要求很高身坐,即使1秒鐘后數(shù)據(jù)更改了秸脱,客戶端也必須展示更改后的數(shù)據(jù)。這種情況如何處理部蛇?

下面我們將對(duì)這種需求摊唇,進(jìn)行解決方案的介紹。順序是這樣的:先從文件類型的緩存入手涯鲁,引入兩個(gè)概念巷查。然后再談下有序,一般數(shù)據(jù)類型比如 JSON 返回值的緩存處理。

文件緩存:借助ETag或Last-Modified判斷文件緩存是否有效

Last-Modified

服務(wù)器的文件存貯岛请,大多采用資源變動(dòng)后就重新生成一個(gè)鏈接的做法旭寿。而且如果你的文件存儲(chǔ)采用的是第三方的服務(wù),比如七牛崇败、青云等服務(wù)盅称,則一定是如此。

這種做法雖然是推薦做法后室,但同時(shí)也不排除不同文件使用同一個(gè)鏈接缩膝。那么如果服務(wù)端的file更改了,本地已經(jīng)有了緩存岸霹。如何更新緩存疾层?

這種情況下需要借助 ETag 或 Last-Modified 判斷圖片緩存是否有效。

Last-Modified 顧名思義贡避,是資源最后修改的時(shí)間戳痛黎,往往與緩存時(shí)間進(jìn)行對(duì)比來(lái)判斷緩存是否過(guò)期。

在瀏覽器第一次請(qǐng)求某一個(gè)URL時(shí)刮吧,服務(wù)器端的返回狀態(tài)會(huì)是200湖饱,內(nèi)容是你請(qǐng)求的資源,同時(shí)有一個(gè)Last-Modified的屬性標(biāo)記此文件在服務(wù)期端最后被修改的時(shí)間皇筛,格式類似這樣:

Last-Modified: Fri, 12 May 2006 18:53:33 GMT

客戶端第二次請(qǐng)求此URL時(shí)琉历,根據(jù) HTTP 協(xié)議的規(guī)定,瀏覽器會(huì)向服務(wù)器傳送 If-Modified-Since 報(bào)頭水醋,詢問(wèn)該時(shí)間之后文件是否有被修改過(guò):

If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT

總結(jié)下來(lái)它的結(jié)構(gòu)如下:

請(qǐng)求 HeaderValue 響應(yīng) HeaderValue
Last-Modified If-Modified-Since

如果服務(wù)器端的資源沒(méi)有變化旗笔,則自動(dòng)返回 HTTP 304 (Not Changed.)狀態(tài)碼,內(nèi)容為空拄踪,這樣就節(jié)省了傳輸數(shù)據(jù)量蝇恶。當(dāng)服務(wù)器端代碼發(fā)生改變或者重啟服務(wù)器時(shí),則重新發(fā)出資源惶桐,返回和第一次請(qǐng)求時(shí)類似撮弧。從而保證不向客戶端重復(fù)發(fā)出資源,也保證當(dāng)服務(wù)器有變化時(shí)姚糊,客戶端能夠得到最新的資源贿衍。

判斷方法用偽代碼表示:

if ETagFromServer != ETagOnClient || LastModifiedFromServer != LastModifiedOnClient

GetFromServer

else

GetFromCache

之所以使用

LastModifiedFromServer != LastModifiedOnClient

而非使用:

LastModifiedFromServer > LastModifiedOnClient

原因是考慮到可能出現(xiàn)類似下面的情況:服務(wù)端可能對(duì)資源文件,廢除其新版救恨,回滾啟用舊版本贸辈,此時(shí)的情況是:

LastModifiedFromServer <= LastModifiedOnClient

但我們依然要更新本地緩存。

參考鏈接: What takes precedence: the ETag or Last-Modified HTTP header?

Demo10和 Demo11 給出了一個(gè)完整的校驗(yàn)步驟:

并給出了 NSURLConnection 和 NSURLSession 兩個(gè)版本:

/*!

@brief 如果本地緩存資源為最新肠槽,則使用使用本地緩存擎淤。如果服務(wù)器已經(jīng)更新或本地?zé)o緩存則從服務(wù)器請(qǐng)求資源奢啥。

@details

步驟:

1. 請(qǐng)求是可變的,緩存策略要每次都從服務(wù)器加載

2. 每次得到響應(yīng)后嘴拢,需要記錄住 LastModified

3. 下次發(fā)送請(qǐng)求的同時(shí)桩盲,將LastModified一起發(fā)送給服務(wù)器(由服務(wù)器比較內(nèi)容是否發(fā)生變化)

@return 圖片資源

*/

- (void)getData:(GetDataCompletion)completion {

NSURL *url = [NSURL URLWithString:kLastModifiedImageURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];

//    // 發(fā)送 etag

//    if (self.etag.length > 0) {

//        [request setValue:self.etag forHTTPHeaderField:@"If-None-Match"];

//    }

// 發(fā)送 LastModified

if (self.localLastModified.length > 0) {

[request setValue:self.localLastModified forHTTPHeaderField:@"If-Modified-Since"];

}

[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

// NSLog(@"%@ %tu", response, data.length);

// 類型轉(zhuǎn)換(如果將父類設(shè)置給子類,需要強(qiáng)制轉(zhuǎn)換)

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

NSLog(@"statusCode == %@", @(httpResponse.statusCode));

// 判斷響應(yīng)的狀態(tài)碼是否是 304 Not Modified (更多狀態(tài)碼含義解釋: https://github.com/ChenYilong/iOSDevelopmentTips)

if (httpResponse.statusCode == 304) {

NSLog(@"加載本地緩存圖片");

// 如果是席吴,使用本地緩存

// 根據(jù)請(qǐng)求獲取到`被緩存的響應(yīng)`赌结!

NSCachedURLResponse *cacheResponse =  [[NSURLCache sharedURLCache] cachedResponseForRequest:request];

// 拿到緩存的數(shù)據(jù)

data = cacheResponse.data;

}

// 獲取并且紀(jì)錄 etag,區(qū)分大小寫(xiě)

//        self.etag = httpResponse.allHeaderFields[@"Etag"];

// 獲取并且紀(jì)錄 LastModified

self.localLastModified = httpResponse.allHeaderFields[@"Last-Modified"];

//        NSLog(@"%@", self.etag);

NSLog(@"%@", self.localLastModified);

dispatch_async(dispatch_get_main_queue(), ^{

!completion ?: completion(data);

});

}] resume];

}

ETag

ETag 是什么孝冒?

HTTP 協(xié)議規(guī)格說(shuō)明定義ETag為“被請(qǐng)求變量的實(shí)體值” (參見(jiàn) —— 章節(jié) 14.19)姑曙。 另一種說(shuō)法是,ETag是一個(gè)可以與Web資源關(guān)聯(lián)的記號(hào)(token)迈倍。它是一個(gè) hash 值,用作 Request 緩存請(qǐng)求頭捣域,每一個(gè)資源文件都對(duì)應(yīng)一個(gè)唯一的 ETag 值啼染,服務(wù)器單獨(dú)負(fù)責(zé)判斷記號(hào)是什么及其含義,并在HTTP響應(yīng)頭中將其傳送到客戶端焕梅,以下是服務(wù)器端返回的格式:

ETag: "50b1c1d4f775c61:df3"

客戶端的查詢更新格式是這樣的:

If-None-Match: W/"50b1c1d4f775c61:df3"

其中:If-None-Match - 與響應(yīng)頭的 Etag 相對(duì)應(yīng)迹鹅,可以判斷本地緩存數(shù)據(jù)是否發(fā)生變化

如果ETag沒(méi)改變,則返回狀態(tài)304然后不返回贞言,這也和Last-Modified一樣斜棚。

總結(jié)下來(lái)它的結(jié)構(gòu)如下:

請(qǐng)求 HeaderValue 響應(yīng) HeaderValue
ETag If-None-Match

ETag 是的功能與 Last-Modified 類似:服務(wù)端不會(huì)每次都會(huì)返回文件資源「么埃客戶端每次向服務(wù)端發(fā)送上次服務(wù)器返回的 ETag 值弟蚀,服務(wù)器會(huì)根據(jù)客戶端與服務(wù)端的 ETag 值是否相等,來(lái)決定是否返回 data酗失,同時(shí)總是返回對(duì)應(yīng)的 HTTP 狀態(tài)碼义钉。客戶端通過(guò) HTTP 狀態(tài)碼來(lái)決定是否使用緩存规肴。比如:服務(wù)端與客戶端的 ETag 值相等捶闸,則 HTTP 狀態(tài)碼為 304,不返回 data拖刃。服務(wù)端文件一旦修改删壮,服務(wù)端與客戶端的 ETag 值不等,并且狀態(tài)值會(huì)變?yōu)?00兑牡,同時(shí)返回 data央碟。

因?yàn)樾薷馁Y源文件后該值會(huì)立即變更。這也決定了 ETag 在斷點(diǎn)下載時(shí)非常有用发绢。

比如 AFNetworking 在進(jìn)行斷點(diǎn)下載時(shí)硬耍,就是借助它來(lái)檢驗(yàn)數(shù)據(jù)的垄琐。詳見(jiàn)在 AFHTTPRequestOperation 類中的用法:

//下載暫停時(shí)提供斷點(diǎn)續(xù)傳功能,修改請(qǐng)求的HTTP頭经柴,記錄當(dāng)前下載的文件位置狸窘,下次可以從這個(gè)位置開(kāi)始下載。

- (void)pause {

unsigned long long offset = 0;

if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {

offset = [[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue];

} else {

offset = [[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length];

}

NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy];

if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) {

//若請(qǐng)求返回的頭部有ETag坯认,則續(xù)傳時(shí)要帶上這個(gè)ETag翻擒,

//ETag用于放置文件的唯一標(biāo)識(shí),比如文件MD5值

//續(xù)傳時(shí)帶上ETag服務(wù)端可以校驗(yàn)相對(duì)上次請(qǐng)求牛哺,文件有沒(méi)有變化陋气,

//若有變化則返回200,回應(yīng)新文件的全數(shù)據(jù)引润,若無(wú)變化則返回206續(xù)傳巩趁。

[mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"];

}

//給當(dāng)前request加Range頭部,下次請(qǐng)求帶上頭部淳附,可以從offset位置繼續(xù)下載

[mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"];

self.request = mutableURLRequest;

[super pause];

}

七牛等第三方文件存儲(chǔ)商現(xiàn)在都已經(jīng)支持ETag议慰,Demo8和9 中給出的演示圖片就是使用的七牛的服務(wù),見(jiàn):

static NSString *const kETagImageURL = @"http://ac-g3rossf7.clouddn.com/xc8hxXBbXexA8LpZEHbPQVB.jpg";
```
下面使用一個(gè) Demo 來(lái)進(jìn)行演示用法奴曙,

以 NSURLConnection 搭配 ETag 為例别凹,步驟如下:

請(qǐng)求的緩存策略使用 NSURLRequestReloadIgnoringCacheData,忽略本地緩存

服務(wù)器響應(yīng)結(jié)束后洽糟,要記錄 Etag炉菲,服務(wù)器內(nèi)容和本地緩存對(duì)比是否變化的重要依據(jù)

在發(fā)送請(qǐng)求時(shí),設(shè)置 If-None-Match坤溃,并且傳入 Etag

連接結(jié)束后拍霜,要判斷響應(yīng)頭的狀態(tài)碼,如果是 304浇雹,說(shuō)明本地緩存內(nèi)容沒(méi)有發(fā)生變化

以下代碼詳見(jiàn) Demo08 :
```
/*!

@brief 如果本地緩存資源為最新沉御,則使用使用本地緩存。如果服務(wù)器已經(jīng)更新或本地?zé)o緩存則從服務(wù)器請(qǐng)求資源昭灵。

@details

步驟:

1. 請(qǐng)求是可變的剃氧,緩存策略要每次都從服務(wù)器加載

2. 每次得到響應(yīng)后滔吠,需要記錄住 etag

3. 下次發(fā)送請(qǐng)求的同時(shí)忠聚,將etag一起發(fā)送給服務(wù)器(由服務(wù)器比較內(nèi)容是否發(fā)生變化)

@return 圖片資源

*/

- (void)getData:(GetDataCompletion)completion {

NSURL *url = [NSURL URLWithString:kETagImageURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];

// 發(fā)送 etag

if (self.etag.length > 0) {

[request setValue:self.etag forHTTPHeaderField:@"If-None-Match"];

}

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

// NSLog(@"%@ %tu", response, data.length);dd

// 類型轉(zhuǎn)換(如果將父類設(shè)置給子類茄菊,需要強(qiáng)制轉(zhuǎn)換)

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

NSLog(@"statusCode == %@", @(httpResponse.statusCode));

// 判斷響應(yīng)的狀態(tài)碼是否是 304 Not Modified (更多狀態(tài)碼含義解釋: https://github.com/ChenYilong/iOSDevelopmentTips)

if (httpResponse.statusCode == 304) {

NSLog(@"加載本地緩存圖片");

// 如果是,使用本地緩存

// 根據(jù)請(qǐng)求獲取到`被緩存的響應(yīng)`抠蚣!

NSCachedURLResponse *cacheResponse =  [[NSURLCache sharedURLCache] cachedResponseForRequest:request];

// 拿到緩存的數(shù)據(jù)

data = cacheResponse.data;

}

// 獲取并且紀(jì)錄 etag祝旷,區(qū)分大小寫(xiě)

self.etag = httpResponse.allHeaderFields[@"Etag"];

NSLog(@"etag值%@", self.etag);

!completion ?: completion(data);

}];

}
```
相應(yīng)的 NSURLSession 搭配 ETag 的版本見(jiàn) Demo09:
```
/*!

@brief 如果本地緩存資源為最新,則使用使用本地緩存。如果服務(wù)器已經(jīng)更新或本地?zé)o緩存則從服務(wù)器請(qǐng)求資源怀跛。

@details

步驟:

1. 請(qǐng)求是可變的距贷,緩存策略要每次都從服務(wù)器加載

2. 每次得到響應(yīng)后,需要記錄住 etag

3. 下次發(fā)送請(qǐng)求的同時(shí)吻谋,將etag一起發(fā)送給服務(wù)器(由服務(wù)器比較內(nèi)容是否發(fā)生變化)

@return 圖片資源

*/

- (void)getData:(GetDataCompletion)completion {

NSURL *url = [NSURL URLWithString:kETagImageURL];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];

// 發(fā)送 etag

if (self.etag.length > 0) {

[request setValue:self.etag forHTTPHeaderField:@"If-None-Match"];

}

[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

// NSLog(@"%@ %tu", response, data.length);

// 類型轉(zhuǎn)換(如果將父類設(shè)置給子類忠蝗,需要強(qiáng)制轉(zhuǎn)換)

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

NSLog(@"statusCode == %@", @(httpResponse.statusCode));

// 判斷響應(yīng)的狀態(tài)碼是否是 304 Not Modified (更多狀態(tài)碼含義解釋: https://github.com/ChenYilong/iOSDevelopmentTips)

if (httpResponse.statusCode == 304) {

NSLog(@"加載本地緩存圖片");

// 如果是,使用本地緩存

// 根據(jù)請(qǐng)求獲取到`被緩存的響應(yīng)`漓拾!

NSCachedURLResponse *cacheResponse =  [[NSURLCache sharedURLCache] cachedResponseForRequest:request];

// 拿到緩存的數(shù)據(jù)

data = cacheResponse.data;

}

// 獲取并且紀(jì)錄 etag阁最,區(qū)分大小寫(xiě)

self.etag = httpResponse.allHeaderFields[@"Etag"];

NSLog(@"%@", self.etag);

dispatch_async(dispatch_get_main_queue(), ^{

!completion ?: completion(data);

});

}] resume];

}
```
運(yùn)行效果:

![687474703a2f2f6936382e74696e797069632e636f6d2f33796868782e6a7067.gif](http://upload-images.jianshu.io/upload_images/3084158-b6a1668ce348ad22.gif?imageMogr2/auto-orient/strip)

###總結(jié)

在官方給出的文檔中提出 ETag 是首選的方式,優(yōu)于 Last-Modified 方式骇两。因?yàn)?ETag 是基于 hash 速种,hash 的規(guī)則可以自己設(shè)置,而且是基于一致性低千,是“強(qiáng)校驗(yàn)”配阵。 Last-Modified 是基于時(shí)間,是弱校驗(yàn)示血,弱在哪里闸餐?比如說(shuō):如果服務(wù)端的資源回滾客戶端的 Last-Modified 反而會(huì)比服務(wù)端還要新。

雖然 ETag 優(yōu)于 Last-Modified 矾芙,但并非所有服務(wù)端都會(huì)支持,而 Last-Modified 則一般都會(huì)有該字段近上。 大多數(shù)情況下需要與服務(wù)端進(jìn)行協(xié)調(diào)支持 ETag 剔宪,如果協(xié)商無(wú)果就只能退而求其次。

Demo 也給出了一個(gè)不支持 ETag 的鏈接壹无,基本隨便找一張圖片都行:
```
static NSString *const kLastModifiedImageURL = @"http://image17-c.poco.cn/mypoco/myphoto/20151211/16/17338872420151211164742047.png";
```
作為通用型的網(wǎng)絡(luò)請(qǐng)求工具 AFNetworking 對(duì)該現(xiàn)狀的處理方式是葱绒,判斷服務(wù)端是否包含 ETag ,然后再進(jìn)行相應(yīng)處理斗锭〉氐恚可見(jiàn) AFHTTPRequestOperation 類中的用法,也就是上文中已經(jīng)給出的斷點(diǎn)下載的代碼岖是。

在回顧下思路:

為資源分派 hash 值帮毁,然后對(duì)比服務(wù)端與本地緩存是否一致來(lái)決定是否需要更新緩存。

這種思路豺撑,在開(kāi)發(fā)中經(jīng)常使用烈疚,比如:處于安全考慮,登陸操作一般不會(huì)傳輸賬號(hào)密碼聪轿,而是傳輸對(duì)應(yīng)的 hash 值-- token 爷肝,這里的 token 就可以看做一個(gè) file 資源,如果想讓一個(gè)用戶登陸超時(shí)時(shí)間是三天,只需要在服務(wù)端每隔三天更改下 token 值灯抛,客戶端與服務(wù)端值不一致金赦,然后服務(wù)端返回 token 過(guò)期的提示。

值得注意的一點(diǎn)是:如果借助了 Last-Modified 和 ETag对嚼,那么緩存策略則必須使用 NSURLRequestReloadIgnoringCacheData 策略夹抗,忽略緩存,每次都要向服務(wù)端進(jìn)行校驗(yàn)猪半。

如果 GET 中包含有版本號(hào)信息

眾多的應(yīng)用都會(huì)在 GET 請(qǐng)求后加上版本號(hào):
```
http://abc.com?my_current_version=v1.0.0
```

這種情況下兔朦,?v1.0 和 ?v2.0 兩個(gè)不同版本,請(qǐng)求到的 Last-Modified 和 ETag 會(huì)如預(yù)期嗎磨确?

這完全取決于公司服務(wù)端同事的實(shí)現(xiàn)沽甥, Last-Modified 和 ETag 僅僅是一個(gè)協(xié)議,并沒(méi)有統(tǒng)一的實(shí)現(xiàn)方法乏奥,而服務(wù)端的處理邏輯完全取決于需求摆舟。

你完全可以要求服務(wù)端同事,僅僅判斷資源的異同邓了,而忽略掉 ?v1.0 和 ?v2.0 兩個(gè)版本的區(qū)別恨诱。

參考鏈接:[if-modified-since vs if-none-match](http://stackoverflow.com/a/1005505)

一般數(shù)據(jù)類型借助 Last-Modified 與 ETag 進(jìn)行緩存

以上的討論是基于文件資源,那么對(duì)一般的網(wǎng)絡(luò)請(qǐng)求是否也能應(yīng)用骗炉?

控制緩存過(guò)期時(shí)間照宝,無(wú)非兩種:設(shè)置一個(gè)過(guò)期時(shí)間;校驗(yàn)緩存與服務(wù)端一致性句葵,只在不一致時(shí)才更新厕鹃。

一般情況下是不會(huì)對(duì) api 層面做這種校驗(yàn),只在有業(yè)務(wù)需求時(shí)才會(huì)考慮做乍丈,比如:

數(shù)據(jù)更新頻率較低剂碴,“萬(wàn)不得已不會(huì)更新”---只在服務(wù)器有更新時(shí)才更新,以此來(lái)保證2G 等惡略網(wǎng)絡(luò)環(huán)境下轻专,有較好的體驗(yàn)忆矛。比如網(wǎng)易新聞欄目,但相反微博列表请垛、新聞列表就不適合催训。

業(yè)務(wù)數(shù)據(jù)一致性要求高,數(shù)據(jù)更新后需要服務(wù)端立刻展示給用戶宗收⊥纾客戶端顯示的數(shù)據(jù)必須是服務(wù)端最新的數(shù)據(jù)

有離線展示需求,必須實(shí)現(xiàn)緩存策略镜雨,保證弱網(wǎng)情況下的數(shù)據(jù)展示的速度嫂侍。但不考慮使用緩存過(guò)期時(shí)間來(lái)控制緩存的有效性儿捧。

盡量減少數(shù)據(jù)傳輸,節(jié)省用戶流量

一些建議:

如果是 file 文件類型挑宠,用 Last-Modified 就夠了菲盾。即使 ETag 是首選,但此時(shí)兩者效果一致各淀。九成以上的需求懒鉴,效果都一致。

如果是一般的數(shù)據(jù)類型--基于查詢的 get 請(qǐng)求碎浇,比如返回值是 data 或 string 類型的 json 返回值临谱。那么 Last-Modified 服務(wù)端支持起來(lái)就會(huì)困難一點(diǎn)。因?yàn)楸热?
你做了一個(gè)博客瀏覽 app 奴璃,查詢最近的10條博客悉默, 基于此時(shí)的業(yè)務(wù)考慮 Last-Modified 指的是10條中任意一個(gè)博客的更改。那么服務(wù)端需要在你發(fā)出請(qǐng)求后苟穆,遍歷下10條數(shù)據(jù)抄课,得到“10條中是否至少一個(gè)被修改了”。而且要保證每一條博客表數(shù)據(jù)都有一個(gè)類似于記錄 Last-Modified 的字段雳旅,這顯然不太現(xiàn)實(shí)跟磨。

如果更新頻率較高,比如最近微博列表攒盈、最近新聞列表抵拘,這些請(qǐng)求就不適合,更多的處理方式是添加一個(gè)接口型豁,客戶端將本地緩存的最后一條數(shù)據(jù)的的時(shí)間戳或 id 傳給服務(wù)端仑濒,然后服務(wù)端會(huì)將新增的數(shù)據(jù)條數(shù)返回,沒(méi)有新增則返回 nil 或 304偷遗。

參考鏈接: [《(慕課網(wǎng))imooc iPhone3.3 接口數(shù)據(jù)緩存》     ](http://www.reibang.com/p/8a4dc775c051)

剩下20%的網(wǎng)絡(luò)緩存需求

真的有NSURLCache 不能滿足的需求?

有人可能要問(wèn):

NSURLCache 不是幫我們做了硬盤(pán)緩存么驼壶?那我們?yōu)槭裁匆约河脭?shù)據(jù)庫(kù)做本地緩存啊氏豌。為啥不直接用NSURLCache 不是更方便?

系統(tǒng)幫我們做的緩存热凹,好處是自動(dòng)泵喘,無(wú)需我們進(jìn)行復(fù)雜的設(shè)置。壞處也恰恰是這個(gè):不夠靈活般妙,不能自定義纪铺。只能指定一個(gè)緩存的總文件夾,不能分別指定每一個(gè)文件緩存的位置碟渺,更不能為每個(gè)文件創(chuàng)建一個(gè)文件夾鲜锚,也不能指定文件夾的名稱。緩存的對(duì)象也是固定的:只能是 GET請(qǐng)求的返回值。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芜繁,一起剝皮案震驚了整個(gè)濱河市旺隙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骏令,老刑警劉巖蔬捷,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異榔袋,居然都是意外死亡周拐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)凰兑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)妥粟,“玉大人,你說(shuō)我怎么就攤上這事聪黎『比荩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵稿饰,是天一觀的道長(zhǎng)锦秒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)喉镰,這世上最難降的妖魔是什么旅择? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮侣姆,結(jié)果婚禮上生真,老公的妹妹穿的比我還像新娘。我一直安慰自己捺宗,他們只是感情好柱蟀,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蚜厉,像睡著了一般长已。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上昼牛,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天术瓮,我揣著相機(jī)與錄音,去河邊找鬼贰健。 笑死胞四,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伶椿。 我是一名探鬼主播辜伟,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼氓侧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了游昼?” 一聲冷哼從身側(cè)響起甘苍,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎烘豌,沒(méi)想到半個(gè)月后载庭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廊佩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年囚聚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片标锄。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡顽铸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出料皇,到底是詐尸還是另有隱情谓松,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布践剂,位于F島的核電站鬼譬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏逊脯。R本人自食惡果不足惜优质,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望军洼。 院中可真熱鬧巩螃,春花似錦、人聲如沸匕争。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甘桑。三九已至拍皮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扇住,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工盗胀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留艘蹋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓票灰,卻偏偏與公主長(zhǎng)得像女阀,于是被迫代替她去往敵國(guó)和親宅荤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容