iOS網(wǎng)絡(luò)緩存掃盲篇

轉(zhuǎn)自:http://blog.csdn.net/ruiwang321/article/details/51036364

iOS網(wǎng)絡(luò)緩存掃盲篇

–使用兩行代碼就能完成80%的緩存需求

下篇預(yù)告:使用80%的代碼來完成剩下的20%的緩存需求 。敬請 star (右上角)持續(xù)關(guān)注队伟。

目錄

當我們在談?wù)摼彺娴臅r候穴吹,我們在談?wù)撌裁矗?/p>

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

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

控制緩存的有效性

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

Last-Modified

ETag

總結(jié)

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

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

由于微信嗜侮、QQ港令、微博、這類的應(yīng)用使用緩存很“重”锈颗,使一般的用戶也對緩存也非常習慣顷霹。緩存已然成為必備。

緩存的目的的以空間換時間

這句話在動輒就是 300M击吱、600M 的大應(yīng)用上淋淀,得到了很好的詮釋。但能有緩存意識的公司覆醇,還在少數(shù)朵纷。

只有你真正感受到痛的時候,你才會考慮使用緩存永脓。

這個痛可能是:

服務(wù)器壓力柴罐、客戶端網(wǎng)絡(luò)優(yōu)化、用戶體驗等等憨奸。

當我們在談?wù)摼彺娴臅r候,我們在談?wù)撌裁矗?/p>

我們今天將站在小白用戶的角度凿试,給緩存這個概念進行重新的定義排宰。

緩存有不同的分類方法:

enter image description here

這里所指的緩存似芝,是一個寬泛的概念。

我們這里主要按照功能進行劃分:

enter image description here

- 第一種 第二種

目的 優(yōu)化型緩存 功能型緩存

具體描述 出于優(yōu)化考慮:服務(wù)器壓力板甘、用戶體驗党瓮、為用戶剩流量等等。同時優(yōu)化型緩存也有內(nèi)存緩存和磁盤緩存之分盐类。 App離線也能查看寞奸,出于功能考慮,屬于存儲范疇

常見概念 GET網(wǎng)絡(luò)請求緩存在跳、WEB緩存 離線存儲

典型應(yīng)用 微信首頁的會話列表枪萄、微信頭像、朋友圈猫妙、網(wǎng)易新聞新聞列表瓷翻、 微信聊天記錄、

Parse對應(yīng)的類 PFCachedQueryController PFOfflineStore

重度使用緩存的App: 微信割坠、微博齐帚、網(wǎng)易新聞、攜程彼哼、去哪兒等等对妄。

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

概述

首先要知道,POST請求不能被緩存敢朱,只有 GET 請求能被緩存剪菱。因為從數(shù)學的角度來講,GET 的結(jié)果是 冪等 的蔫饰,就好像字典里的 key 與 value 就是冪等的琅豆,而 POST 不 冪等 。緩存的思路就是將查詢的參數(shù)組成的值作為 key 篓吁,對應(yīng)結(jié)果作為value茫因。從這個意義上說,一個文件的資源鏈接杖剪,也叫 GET 請求冻押,下文也會這樣看待。

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

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

第一個步驟:請使用 GET 請求盛嘿。

第二個步驟:

如果你已經(jīng)使用 了 GET 請求洛巢,iOS 系統(tǒng) SDK 已經(jīng)幫你做好了緩存。你需要的僅僅是設(shè)置下內(nèi)存緩存大小次兆、磁盤緩存大小稿茉、以及緩存路徑。甚至這兩行代碼不設(shè)置也是可以的,會有一個默認值漓库。代碼如下:

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

[NSURLCache setSharedURLCache:urlCache];

第三個步驟:沒有第三步恃慧!

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

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

(AFN 是不是在暗諷 SDWebImage 復(fù)雜又蹩腳的緩存機制茂装?怠蹂?)

要注意

iOS 5.0開始,支持磁盤緩存少态,但僅支持 HTTP

iOS 6.0開始城侧,支持 HTTPS 緩存

控制緩存的有效性

我們知道:

只要是緩存,總會過期况增。

那么緩存的過期時間如何控制赞庶?

上文中的兩行代碼,已經(jīng)給出了一個方法澳骤,指定超時時間歧强。但這并也許不能滿足我們的需求,如果我們對數(shù)據(jù)的一致性为肮,時效性要求很高摊册,即使1秒鐘后數(shù)據(jù)更改了,客戶端也必須展示更改后的數(shù)據(jù)颊艳。這種情況如何處理茅特?

下面我們將對這種需求,進行解決方案的介紹棋枕。順序是這樣的:先從文件類型的緩存入手白修,引入兩個概念。然后再談下重斑,一般數(shù)據(jù)類型比如 JSON 返回值的緩存處理兵睛。

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

Last-Modified

服務(wù)器的文件存貯,大多采用資源變動后就重新生成一個鏈接的做法窥浪。而且如果你的文件存儲采用的是第三方的服務(wù)祖很,比如七牛、青云等服務(wù)漾脂,則一定是如此假颇。

這種做法雖然是推薦做法,但同時也不排除不同文件使用同一個鏈接骨稿。那么如果服務(wù)端的file更改了笨鸡,本地已經(jīng)有了緩存姜钳。如何更新緩存?

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

Last-Modified 顧名思義傲须,是資源最后修改的時間戳,往往與緩存時間進行對比來判斷緩存是否過期趟脂。

在瀏覽器第一次請求某一個URL時,服務(wù)器端的返回狀態(tài)會是200例衍,內(nèi)容是你請求的資源昔期,同時有一個Last-Modified的屬性標記此文件在服務(wù)期端最后被修改的時間,格式類似這樣:

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

客戶端第二次請求此URL時佛玄,根據(jù) HTTP 協(xié)議的規(guī)定硼一,瀏覽器會向服務(wù)器傳送 If-Modified-Since 報頭,詢問該時間之后文件是否有被修改過:

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

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

請求 HeaderValue 響應(yīng) HeaderValue

Last-Modified If-Modified-Since

如果服務(wù)器端的資源沒有變化梦抢,則自動返回 HTTP 304 (Not Changed.)狀態(tài)碼般贼,內(nèi)容為空,這樣就節(jié)省了傳輸數(shù)據(jù)量奥吩。當服務(wù)器端代碼發(fā)生改變或者重啟服務(wù)器時哼蛆,則重新發(fā)出資源,返回和第一次請求時類似霞赫。從而保證不向客戶端重復(fù)發(fā)出資源腮介,也保證當服務(wù)器有變化時,客戶端能夠得到最新的資源端衰。

判斷方法用偽代碼表示:

if ETagFromServer != ETagOnClient || LastModifiedFromServer != LastModifiedOnClient

GetFromServer

else

GetFromCache

之所以使用

LastModifiedFromServer != LastModifiedOnClient

而非使用:

LastModifiedFromServer > LastModifiedOnClient

原因是考慮到可能出現(xiàn)類似下面的情況:服務(wù)端可能對資源文件叠洗,廢除其新版,回滾啟用舊版本旅东,此時的情況是:

LastModifiedFromServer <= LastModifiedOnClient

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

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

Demo10和 Demo11 給出了一個完整的校驗步驟:

并給出了 NSURLConnection 和 NSURLSession 兩個版本:

/*!

@brief 如果本地緩存資源為最新,則使用使用本地緩存抵代。如果服務(wù)器已經(jīng)更新或本地無緩存則從服務(wù)器請求資源腾节。

@details

步驟:

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

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

3. 下次發(fā)送請求的同時禀倔,將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è)置給子類,需要強制轉(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ù)請求獲取到`被緩存的響應(yīng)`救湖!

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

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

data = cacheResponse.data;

}

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

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

// 獲取并且紀錄 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ī)格說明定義ETag為“被請求變量的實體值” (參見 —— 章節(jié) 14.19)鞋既。 另一種說法是力九,ETag是一個可以與Web資源關(guān)聯(lián)的記號(token)。它是一個 hash 值邑闺,用作 Request 緩存請求頭跌前,每一個資源文件都對應(yīng)一個唯一的 ETag 值,

服務(wù)器單獨負責判斷記號是什么及其含義陡舅,并在HTTP響應(yīng)頭中將其傳送到客戶端抵乓,以下是服務(wù)器端返回的格式:

ETag: "50b1c1d4f775c61:df3"

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

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

其中:

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

如果ETag沒改變靶衍,則返回狀態(tài)304然后不返回灾炭,這也和Last-Modified一樣。

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

請求 HeaderValue 響應(yīng) HeaderValue

ETag If-None-Match

ETag 是的功能與 Last-Modified 類似:服務(wù)端不會每次都會返回文件資源颅眶◎诔觯客戶端每次向服務(wù)端發(fā)送上次服務(wù)器返回的 ETag 值,服務(wù)器會根據(jù)客戶端與服務(wù)端的 ETag 值是否相等涛酗,來決定是否返回 data铡原,同時總是返回對應(yīng)的 HTTP 狀態(tài)碼∩烫荆客戶端通過 HTTP 狀態(tài)碼來決定是否使用緩存燕刻。比如:服務(wù)端與客戶端的 ETag 值相等,則 HTTP 狀態(tài)碼為 304沈自,不返回 data酌儒。服務(wù)端文件一旦修改,服務(wù)端與客戶端的 ETag 值不等枯途,并且狀態(tài)值會變?yōu)?00忌怎,同時返回 data。

因為修改資源文件后該值會立即變更酪夷。這也決定了 ETag 在斷點下載時非常有用榴啸。

比如 AFNetworking 在進行斷點下載時,就是借助它來檢驗數(shù)據(jù)的晚岭。詳見在 AFHTTPRequestOperation 類中的用法:

//下載暫停時提供斷點續(xù)傳功能鸥印,修改請求的HTTP頭,記錄當前下載的文件位置坦报,下次可以從這個位置開始下載库说。

- (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"]) {

//若請求返回的頭部有ETag,則續(xù)傳時要帶上這個ETag片择,

//ETag用于放置文件的唯一標識潜的,比如文件MD5值

//續(xù)傳時帶上ETag服務(wù)端可以校驗相對上次請求,文件有沒有變化字管,

//若有變化則返回200啰挪,回應(yīng)新文件的全數(shù)據(jù)信不,若無變化則返回206續(xù)傳。

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

}

//給當前request加Range頭部亡呵,下次請求帶上頭部抽活,可以從offset位置繼續(xù)下載

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

self.request = mutableURLRequest;

[super pause];

}

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

static NSString *const kETagImageURL = @"http://ac-g3rossf7.clouddn.com/xc8hxXBbXexA8LpZEHbPQVB.jpg";

下面使用一個 Demo 來進行演示用法下硕,

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

請求的緩存策略使用 NSURLRequestReloadIgnoringCacheData汁胆,忽略本地緩存

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

在發(fā)送請求時沦泌,設(shè)置 If-None-Match,并且傳入 Etag

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

以下代碼詳見 Demo08 :

/*!

@brief 如果本地緩存資源為最新萝衩,則使用使用本地緩存回挽。如果服務(wù)器已經(jīng)更新或本地無緩存則從服務(wù)器請求資源。

@details

步驟:

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

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

3. 下次發(fā)送請求的同時,將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è)置給子類牌捷,需要強制轉(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ù)請求獲取到`被緩存的響應(yīng)`!

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

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

data = cacheResponse.data;

}

// 獲取并且紀錄 etag暗甥,區(qū)分大小寫

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

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

!completion ?: completion(data);

}];

}

相應(yīng)的 NSURLSession 搭配 ETag 的版本見 Demo09:

/*!

@brief 如果本地緩存資源為最新喜滨,則使用使用本地緩存。如果服務(wù)器已經(jīng)更新或本地無緩存則從服務(wù)器請求資源撤防。

@details

步驟:

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

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

3. 下次發(fā)送請求的同時寄月,將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è)置給子類辜膝,需要強制轉(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ù)請求獲取到`被緩存的響應(yīng)`漾肮!

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

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

data = cacheResponse.data;

}

// 獲取并且紀錄 etag厂抖,區(qū)分大小寫

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

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

dispatch_async(dispatch_get_main_queue(), ^{

!completion ?: completion(data);

});

}] resume];

}

運行效果:

enter image description here

總結(jié)

在官方給出的文檔中提出 ETag 是首選的方式,優(yōu)于 Last-Modified 方式初橘。因為 ETag 是基于 hash 验游,hash 的規(guī)則可以自己設(shè)置充岛,而且是基于一致性,是“強校驗”耕蝉。 Last-Modified 是基于時間崔梗,是弱校驗,弱在哪里垒在?比如說:如果服務(wù)端的資源回滾客戶端的 Last-Modified 反而會比服務(wù)端還要新蒜魄。

雖然 ETag 優(yōu)于 Last-Modified ,但并非所有服務(wù)端都會支持场躯,而 Last-Modified 則一般都會有該字段谈为。 大多數(shù)情況下需要與服務(wù)端進行協(xié)調(diào)支持 ETag ,如果協(xié)商無果就只能退而求其次踢关。

Demo 也給出了一個不支持 ETag 的鏈接伞鲫,基本隨便找一張圖片都行:

static NSString *const kLastModifiedImageURL = @"http://image17-c.poco.cn/mypoco/myphoto/20151211/16/17338872420151211164742047.png";

1

1

作為通用型的網(wǎng)絡(luò)請求工具 AFNetworking 對該現(xiàn)狀的處理方式是,判斷服務(wù)端是否包含 ETag 签舞,然后再進行相應(yīng)處理秕脓。可見 AFHTTPRequestOperation 類中的用法儒搭,也就是上文中已經(jīng)給出的斷點下載的代碼吠架。

在回顧下思路:

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

這種思路傍药,在開發(fā)中經(jīng)常使用,比如:處于安全考慮魂仍,登陸操作一般不會傳輸賬號密碼拐辽,而是傳輸對應(yīng)的 hash 值– token ,這里的 token 就可以看做一個 file 資源蓄诽,如果想讓一個用戶登陸超時時間是三天薛训,只需要在服務(wù)端每隔三天更改下 token 值,客戶端與服務(wù)端值不一致仑氛,然后服務(wù)端返回 token 過期的提示乙埃。

值得注意的一點是:

如果借助了 Last-Modified 和 ETag,那么緩存策略則必須使用 NSURLRequestReloadIgnoringCacheData 策略锯岖,忽略緩存介袜,每次都要向服務(wù)端進行校驗。

如果 GET 中包含有版本號信息

眾多的應(yīng)用都會在 GET 請求后加上版本號:

http://abc.com?my_current_version=v1.0.0

1

1

這種情況下出吹,

?v1.0 和 ?v2.0 兩個不同版本遇伞,請求到的 Last-Modified 和 ETag 會如預(yù)期嗎?

這完全取決于公司服務(wù)端同事的實現(xiàn)捶牢, Last-Modified 和 ETag 僅僅是一個協(xié)議鸠珠,并沒有統(tǒng)一的實現(xiàn)方法巍耗,而服務(wù)端的處理邏輯完全取決于需求。

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

參考鏈接:if-modified-since vs if-none-match

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

以上的討論是基于文件資源驯耻,那么對一般的網(wǎng)絡(luò)請求是否也能應(yīng)用亲族?

控制緩存過期時間,無非兩種:設(shè)置一個過期時間可缚;校驗緩存與服務(wù)端一致性霎迫,只在不一致時才更新。

一般情況下是不會對 api 層面做這種校驗帘靡,只在有業(yè)務(wù)需求時才會考慮做知给,比如:

數(shù)據(jù)更新頻率較低,“萬不得已不會更新”—只在服務(wù)器有更新時才更新描姚,以此來保證2G 等惡略網(wǎng)絡(luò)環(huán)境下炼鞠,有較好的體驗。比如網(wǎng)易新聞欄目轰胁,但相反微博列表、新聞列表就不適合朝扼。

業(yè)務(wù)數(shù)據(jù)一致性要求高赃阀,數(shù)據(jù)更新后需要服務(wù)端立刻展示給用戶∏嬗保客戶端顯示的數(shù)據(jù)必須是服務(wù)端最新的數(shù)據(jù)

有離線展示需求榛斯,必須實現(xiàn)緩存策略,保證弱網(wǎng)情況下的數(shù)據(jù)展示的速度搂捧。但不考慮使用緩存過期時間來控制緩存的有效性驮俗。

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

一些建議:

如果是 file 文件類型允跑,用 Last-Modified 就夠了王凑。即使 ETag 是首選,但此時兩者效果一致聋丝。九成以上的需求索烹,效果都一致。

如果是一般的數(shù)據(jù)類型–基于查詢的 get 請求弱睦,比如返回值是 data 或 string 類型的 json 返回值百姓。那么 Last-Modified 服務(wù)端支持起來就會困難一點。因為比如

你做了一個博客瀏覽 app 况木,查詢最近的10條博客垒拢, 基于此時的業(yè)務(wù)考慮 Last-Modified 指的是10條中任意一個博客的更改旬迹。那么服務(wù)端需要在你發(fā)出請求后,遍歷下10條數(shù)據(jù)求类,得到“10條中是否至少一個被修改了”奔垦。而且要保證每一條博客表數(shù)據(jù)都有一個類似于記錄 Last-Modified 的字段,這顯然不太現(xiàn)實仑嗅。

如果更新頻率較高宴倍,比如最近微博列表、最近新聞列表仓技,這些請求就不適合鸵贬,更多的處理方式是添加一個接口,客戶端將本地緩存的最后一條數(shù)據(jù)的的時間戳或 id 傳給服務(wù)端脖捻,然后服務(wù)端會將新增的數(shù)據(jù)條數(shù)返回阔逼,沒有新增則返回 nil 或 304。

參考鏈接: 《(慕課網(wǎng))imooc iPhone3.3 接口數(shù)據(jù)緩存》

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

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

有人可能要問:

NSURLCache 不是幫我們做了硬盤緩存么嗜浮?那我們?yōu)槭裁匆约河脭?shù)據(jù)庫做本地緩存啊。為啥不直接用NSURLCache 不是更方便摩疑?

系統(tǒng)幫我們做的緩存危融,好處是自動,無需我們進行復(fù)雜的設(shè)置雷袋。壞處也恰恰是這個:不夠靈活吉殃,不能自定義。只能指定一個緩存的總文件夾楷怒,不能分別指定每一個文件緩存的位置蛋勺,更不能為每個文件創(chuàng)建一個文件夾,也不能指定文件夾的名稱鸠删。緩存的對象也是固定的:只能是 GET請求的返回值抱完。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刃泡,隨后出現(xiàn)的幾起案子巧娱,更是在濱河造成了極大的恐慌,老刑警劉巖烘贴,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件家卖,死亡現(xiàn)場離奇詭異,居然都是意外死亡庙楚,警方通過查閱死者的電腦和手機上荡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人酪捡,你說我怎么就攤上這事叁征。” “怎么了逛薇?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵捺疼,是天一觀的道長。 經(jīng)常有香客問我永罚,道長啤呼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任呢袱,我火速辦了婚禮官扣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘羞福。我一直安慰自己惕蹄,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布治专。 她就那樣靜靜地躺著卖陵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪张峰。 梳的紋絲不亂的頭發(fā)上泪蔫,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音喘批,去河邊找鬼鸥滨。 笑死,一個胖子當著我的面吹牛谤祖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播老速,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粥喜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了橘券?” 一聲冷哼從身側(cè)響起额湘,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旁舰,沒想到半個月后锋华,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡箭窜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年毯焕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡纳猫,死狀恐怖婆咸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芜辕,我是刑警寧澤尚骄,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站侵续,受9級特大地震影響倔丈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜状蜗,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一需五、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诗舰,春花似錦警儒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至属百,卻和暖如春记劝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背族扰。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工厌丑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渔呵。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓怒竿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扩氢。 傳聞我的和親對象是個殘疾皇子耕驰,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

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