淺談iOS 網(wǎng)絡(luò)緩存

最近公司項目安全自查, 就對現(xiàn)有項目詳細的檢查了一下, 其中就包括網(wǎng)絡(luò)請求緩存安全這一塊, 所以就查資料詳細的學(xué)習(xí)了一下這一方面的知識, 這里做個記錄也和想要了解這一塊知識的同學(xué)做個分享

初識緩存

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

緩存有不同的分類方法:

按照功能分: 1. 優(yōu)化型緩存 2. 存儲型緩存
按照形式劃分: 1. 內(nèi)存型緩存 2. 磁盤型緩存(iOS 5以后支持)

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

POST請求不能被緩存叛赚,只有 GET 請求能被緩存饵逐。因為從數(shù)學(xué)的角度來講闻蛀,GET 的結(jié)果是 冪等 的厕妖,就好像字典里的 key 與 value 就是冪等的溉旋,而 POST 不 冪等 航唆。緩存的思路就是將查詢的參數(shù)組成的值作為 key 坠七,對應(yīng)結(jié)果作為value誊稚。從這個意義上說,一個文件的資源鏈接倒脓,也叫 GET 請求撑螺,下文也會這樣看待。

緩存只需要二步

第一個步驟:請使用 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];

如何控制緩存的有效性

借助 ETag 或 Last-Modified 判斷緩存是否有效。

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

如果服務(wù)器端的資源沒有變化咒精,則自動返回 HTTP 304 (Not Changed.)狀態(tài)碼镶柱,內(nèi)容為空,這樣就節(jié)省了傳輸數(shù)據(jù)量模叙。當服務(wù)器端代碼發(fā)生改變或者重啟服務(wù)器時歇拆,則重新發(fā)出資源,返回和第一次請求時類似范咨。從而保證不向客戶端重復(fù)發(fā)出資源故觅,也保證當服務(wù)器有變化時,客戶端能夠得到最新的資源渠啊。

ETag

HTTP 協(xié)議規(guī)格說明定義ETag為“被請求變量的實體值” (參見 —— 章節(jié) 14.19)输吏。 另一種說法是,ETag是一個可以與Web資源關(guān)聯(lián)的記號(token)替蛉。它是一個 hash 值贯溅,用作 Request 緩存請求頭,每一個資源文件都對應(yīng)一個唯一的 ETag 值躲查,
服務(wù)器單獨負責(zé)判斷記號是什么及其含義它浅,并在HTTP響應(yīng)頭中將其傳送到客戶端,以下是服務(wù)器端返回的格式:

 ETag: "50b1c1d4f775c61:df3"


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

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

如果ETag沒改變镣煮,則返回狀態(tài)304然后不返回姐霍,這也和Last-Modified一樣。

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ù)的

示例代碼:

/*!
 @brief 如果本地緩存資源為最新矛双,則使用使用本地緩存渊抽。如果服務(wù)器已經(jīng)更新或本地?zé)o緩存則從服務(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 代碼

 /*!
 @brief 如果本地緩存資源為最新玩焰,則使用使用本地緩存。如果服務(wù)器已經(jīng)更新或本地?zé)o緩存則從服務(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];
}

iOSNSURLRequest提供了7種緩存策略

NSURLRequestUseProtocolCachePolicy // 默認的緩存策略(取決于協(xié)議)

NSURLRequestReloadIgnoringLocalCacheData // 忽略緩存,重新請求

NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未實現(xiàn)

NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 忽略緩存嗅钻,
重新請求

NSURLRequestReturnCacheDataElseLoad// 有緩存就用緩存皂冰,沒有緩存就重新請求

NSURLRequestReturnCacheDataDontLoad// 有緩存就用緩存,沒有緩存就不發(fā)請求养篓,當做請求出錯處理(用于離線模式)

NSURLRequestReloadRevalidatingCacheData // 未實現(xiàn)

使用緩存策略的簡單示例

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 1.創(chuàng)建請求
    NSURL *url = [NSURL URLWithString:@"http://127.0.0.1:8080/YYServer/video"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    // 2.設(shè)置緩存策略(有緩存就用緩存秃流,沒有緩存就重新請求)
    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
    
    // 3.發(fā)送請求
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        if (data) {
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
            
            NSLog(@"%@", dict);
        }
    }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市柳弄,隨后出現(xiàn)的幾起案子舶胀,更是在濱河造成了極大的恐慌概说,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚣伐,死亡現(xiàn)場離奇詭異糖赔,居然都是意外死亡,警方通過查閱死者的電腦和手機轩端,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門放典,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人基茵,你說我怎么就攤上這事奋构。” “怎么了拱层?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵弥臼,是天一觀的道長。 經(jīng)常有香客問我根灯,道長醋火,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任箱吕,我火速辦了婚禮芥驳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茬高。我一直安慰自己兆旬,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布怎栽。 她就那樣靜靜地躺著丽猬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪熏瞄。 梳的紋絲不亂的頭發(fā)上脚祟,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音强饮,去河邊找鬼由桌。 笑死,一個胖子當著我的面吹牛邮丰,可吹牛的內(nèi)容都是我干的行您。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼剪廉,長吁一口氣:“原來是場噩夢啊……” “哼娃循!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起斗蒋,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤捌斧,失蹤者是張志新(化名)和其女友劉穎笛质,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捞蚂,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡妇押,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洞难。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舆吮。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡揭朝,死狀恐怖队贱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情潭袱,我是刑警寧澤柱嫌,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站屯换,受9級特大地震影響编丘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜彤悔,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一嘉抓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晕窑,春花似錦抑片、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疾牲,卻和暖如春植捎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阳柔。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工焰枢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舌剂。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓医咨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親架诞。 傳聞我的和親對象是個殘疾皇子拟淮,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

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