iOS網(wǎng)絡(luò)請求緩存 - NSURLCache

NSURLCache

NSURLCache為應(yīng)用程序的URL請求提供復(fù)合的內(nèi)存和磁盤緩存機制哪审。作為Foundation的URL加載系統(tǒng)的一部分组贺,任何加載的請求NSURLConnection都將由NSURLCache完成
網(wǎng)絡(luò)緩存減少了需要對服務(wù)器發(fā)出的請求數(shù)量扇售,并改善了脫機使用應(yīng)用程序或在慢速網(wǎng)絡(luò)條件下使用應(yīng)用程序的體驗。
當請求從服務(wù)器加載完其響應(yīng)后,緩存的響應(yīng)將保存在本地已添。下次發(fā)出相同的請求時朝巫,將立即返回本地緩存的響應(yīng)鸿摇,而不連接到服務(wù)器。自動且透明地NSURLCache返回緩存的響應(yīng)劈猿。

NSURLCache提供的是內(nèi)存以及磁盤的綜合緩存機制拙吉,使用NSURLCache之前需要在AppDelegate中緩存空間的設(shè)置:

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                       diskCapacity:20 * 1024 * 1024
                                                           diskPath:nil];
  [NSURLCache setSharedURLCache:URLCache];
}

緩存策略
NSURLRequestCachePolicy
NSURLRequest有一個屬性,它根據(jù)以下常量指定請求的緩存行為:cachePolicy

  • NSURLRequestUseProtocolCachePolicy:協(xié)議實現(xiàn)中定義的緩存邏輯用于特定的URL加載請求揪荣。這是默認策略筷黔。
  • NSURLRequestReloadIgnoringLocalCacheData:應(yīng)從原始來源加載數(shù)據(jù)。不應(yīng)使用現(xiàn)有的緩存數(shù)據(jù)仗颈。
  • NSURLRequestReloadIgnoringLocalAndRemoteCacheData:不僅應(yīng)該忽略本地緩存數(shù)據(jù)佛舱,而且應(yīng)該指示代理和其他中間體在協(xié)議允許的情況下忽略它們的緩存。
  • NSURLRequestReturnCacheDataElseLoad:應(yīng)使用現(xiàn)有的緩存數(shù)據(jù)挨决,無論其年齡或到期日期如何请祖。如果對應(yīng)于請求的高速緩存中沒有現(xiàn)有數(shù)據(jù),則從始發(fā)源加載數(shù)據(jù)脖祈。
  • NSURLRequestReturnCacheDataDontLoad:應(yīng)使用現(xiàn)有緩存數(shù)據(jù)肆捕,無論其年齡或到期日期如何。如果對應(yīng)于請求的高速緩存中沒有現(xiàn)有數(shù)據(jù)盖高,則不嘗試從始發(fā)源加載數(shù)據(jù)慎陵,并且認為負載已經(jīng)失敗(即“離線”模式)喻奥。
  • NSURLRequestReloadRevalidatingCacheData:如果源源確認其有效性席纽,則可以使用現(xiàn)有的高速緩存數(shù)據(jù),否則從原始源加載URL撞蚕。

UseProtocolCachePolicy 默認行為
ReloadIgnoringLocalAndRemoteCacheData 不要使用緩存
ReturnCacheDataElseLoad 使用緩存(無論過時)润梯,或者如果不存在緩存響應(yīng),請從網(wǎng)絡(luò)加載
ReturnCacheDataDontLoad 離線模式:使用緩存(無論過時)诈豌,但不要從網(wǎng)絡(luò)加載
ReloadRevalidatingCacheData 使用前驗證服務(wù)器的緩存

如果本地沒有緩存數(shù)據(jù)仆救,則進行網(wǎng)絡(luò)請求。如果本地有緩存矫渔,并且緩存沒有失效彤蔽,則使用緩存。如果緩存已經(jīng)失效庙洼,則詢問服務(wù)器數(shù)據(jù)是否改變顿痪,如果沒改變镊辕,依然使用緩存,如果改變了則請求新數(shù)據(jù)蚁袭。如果沒有指定是否失效征懈,那么系統(tǒng)將自己判斷緩存是否失效。(通常認為是6-24小時的有效時間)
默認情況下NSURLCache的緩存策略是根據(jù)http協(xié)議來的揩悄,服務(wù)器通過Cache-Control: max-age字段來告訴NSURLCache是否需要緩存數(shù)據(jù)

HTTP緩存語義

因為NSURLConnection旨在支持多種協(xié)議 - 包括兩者FTP和HTTP/ HTTPS- URL加載系統(tǒng)API以協(xié)議無關(guān)的方式指定緩存卖哎。出于本文的目的,將根據(jù)HTTP語義來解釋緩存删性。

HTTP請求和響應(yīng)使用標頭來傳遞元數(shù)據(jù)亏娜,如字符編碼,MIME類型和緩存指令蹬挺。

請求緩存標頭

默認情況下维贺,NSURLRequest將使用當前時間來確定是否應(yīng)返回緩存的響應(yīng)。要獲得更精確的緩存控制巴帮,可以指定以下標頭:

  • If-Modified-Since- 此請求標頭對應(yīng)于Last-Modified響應(yīng)標頭溯泣。將this的Last-Modified值設(shè)置為從上次請求到同一端點的值。
  • If-None-Match- 此請求標頭對應(yīng)于Etag響應(yīng)標頭榕茧。使用Etag先前收到的對該端點的最后一個請求的值垃沦。

響應(yīng)緩存標頭
一個NSHTTPURLResponse包含一組HTTP標頭,其中可以包含以下有關(guān)如何緩存該響應(yīng)的指令:

  • Cache-Control - 此標頭必須存在于服務(wù)器的響應(yīng)中用押,以啟用客戶端的HTTP緩存栏尚。此標頭的值可能包括類似其信息max-age(緩存響應(yīng)的時間長度),以及響應(yīng)是否可以緩存public或private訪問只恨,或者no-cache(根本不)。有關(guān)完整詳細信息抬虽,請參閱Cache-ControlRFC 2616部分官觅。
    除此之外Cache-Control,服務(wù)器還可以發(fā)送可用于根據(jù)需要有條件地請求信息的附加標頭(如上一節(jié)所述):

  • Last-Modified - 此標頭的值對應(yīng)于上次更改所請求資源的日期和時間阐污。例如休涤,如果客戶端請求最近照片的時間線/photos/timeline,則Last-Modified 可以將值設(shè)置為拍攝最近照片的時間笛辟。

  • Etag - “實體標簽”的縮寫功氨,這是表示請求內(nèi)容資源的標識符。實際上手幢,Etag標頭值可能類似于MD5資源屬性的摘要捷凄。這對于可能沒有明顯Last-Modified價值的動態(tài)生成的資源特別有用。

NSURLConnectionDelegate

收到服務(wù)器響應(yīng)后围来,NSURLConnection委托人就有機會指定緩存的響應(yīng)跺涤。-connection:willCacheResponse:

NSCachedURLResponse是一個包含與響應(yīng)關(guān)聯(lián)NSURLResponse的緩存的類NSData匈睁。

在,該對象已根據(jù)URL連接的結(jié)果自動創(chuàng)建桶错。因為沒有可變對應(yīng)物航唆,為了改變?nèi)魏螙|西,必須構(gòu)造一個新對象院刁,將任何修改后的值傳遞給糯钙,例如:-connection:willCacheResponse:cachedResponseNSCachedURLResponsecachedResponse–initWithResponse:data:userInfo:storagePolicy:

- (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];
}

如果返回,則不會緩存響應(yīng)退腥。-connection:willCacheResponse:nil

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
    return nil;
}

如果沒有實現(xiàn)任岸,NSURLConnection只會使用否則將傳入的緩存響應(yīng),因此除非您需要更改或阻止緩存阅虫,否則不需要在委托中實現(xiàn)此方法演闭。-connection:willCacheResponse:

API

@property(class, strong) NSURLCache *sharedURLCache

返回NSURLCache的單例,這是一個類屬性颓帝;
沒有特殊緩存要求或約束的應(yīng)用可以直接使用此實例米碰。 具有更多特定需求的應(yīng)用可以創(chuàng)建一個自定義的NSURLCache對象,并調(diào)用setSharedURLCache:方法將其設(shè)置為共享緩存實例购城。 所有對共享實例的調(diào)用都應(yīng)該在此設(shè)置之后吕座。

//Creating a new cache object
- (instancetype)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path

使用指定的值來初始化NSURLCache對象;

memoryCapacity: 內(nèi)存緩存的字節(jié)數(shù)瘪板;
diskCapacity: 磁盤緩存的字節(jié)數(shù)吴趴;
path:在macOS中,path是存儲磁盤緩存的位置侮攀。在iOS中锣枝,path是應(yīng)用程序的默認緩存目錄的子目錄的名稱,用于存儲磁盤緩存(如果不存在則創(chuàng)建子目錄)兰英。

生成的NSURLCache支持磁盤存取撇叁,因此開發(fā)人員在選擇此類緩存的容量時可以更加寬松。 在大多數(shù)情況下畦贸,以幾十兆字節(jié)的磁盤緩存應(yīng)該是可以接受的陨闹。


//Getting and storing cached objects
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request

返回緩存的指定request的的響應(yīng)數(shù)據(jù);如果不存在返回nil;

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request

緩存指定的request對應(yīng)的cachedResponse


//Removing cached objects
- (void)removeAllCachedResponses

刪除所有的緩存數(shù)據(jù)薄坏;

- (void)removeCachedResponseForRequest:(NSURLRequest *)request

清空指定request對應(yīng)的緩存的響應(yīng)數(shù)據(jù)趋厉,如果本來就沒有則什么都不做;


//Getting and setting on-disk cache properties
@property(readonly) NSUInteger currentDiskUsage

調(diào)用者的磁盤緩存的當前已使用的大薪鹤埂(以字節(jié)為單位)君账。


@property NSUInteger diskCapacity

調(diào)用者的磁盤緩存的總?cè)萘浚ㄒ宰止?jié)為單位)。


//Getting and setting in-memory cache properties
@property(readonly) NSUInteger currentMemoryUsage 和 @property NSUInteger memoryCapacity

同上涵但;


//Instance Methods
- (void)getCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler

這個是寫在NSURLSessionTaskAdditions分類中的方法杈绸,沒有注釋和相關(guān)文檔帖蔓,下面的方法也是一樣的⊥В看方法名塑娇,作用應(yīng)該是取出指定任務(wù)中的所有緩存的響應(yīng)數(shù)據(jù);


- (void)removeCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask

刪除指定任務(wù)中的緩存數(shù)據(jù)劫侧;


- (void)removeCachedResponsesSinceDate:(NSDate *)date

刪除指定時間內(nèi)的緩存數(shù)據(jù)埋酬;


- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forDataTask:(NSURLSessionDataTask *)dataTask

存儲指定任務(wù)的響應(yīng)數(shù)據(jù);

測試代碼如下:

//
//  MOBUrlCache.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MOBUrlCache : NSURLCache

@end

NS_ASSUME_NONNULL_END



#import "MOBUrlCache.h"

@implementation MOBUrlCache

/*
 
 注意:NSURLCache緩存不緩存request及response烧栋,不是由request得緩存策略決定的写妥,緩存策略只是說明是否加載已經(jīng)存在的緩存,緩存機制有URL Loading System提供审姓。
 */

// CusURLCache告訴系統(tǒng)我有怎樣的已緩存的NSCachedURLResponse對象(或者沒有)珍特,這里攔截了http://img1.gtimg.com/news/pics/hv1/138/183/2205/143426928.jpeg,并且做了假的NSURLResponse以供緩存
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
    
    
    
    NSURL *url = [request URL];
    NSLog(@"+++++++++++request.string = %@",request.URL.absoluteString);
    
    if ([url.absoluteString isEqualToString:@"http://img1.gtimg.com/news/pics/hv1/138/183/2205/143426928.jpeg"]) {
        
        NSLog(@"+++++++++request = %@",request);
        
        NSURLResponse *response =
        [[NSURLResponse alloc] initWithURL:url
                                  MIMEType:@"text/plain"
                     expectedContentLength:1
                          textEncodingName:nil];
        
        NSCachedURLResponse *cachedResponse =
        [[NSCachedURLResponse alloc] initWithResponse:response
                                                 data:[NSData dataWithBytes:" " length:1]];
        // 有人可能會認為只需要返回假的響應(yīng)對象就夠了魔吐,沒必要緩存它扎筒。但這樣會因響應(yīng)對象被系統(tǒng)釋放而導(dǎo)致app crash。不知道為何為會這樣酬姆,可能是iOS的bug(Mac OS X 10.5.x也存在同樣問題嗜桌,而10.4.x及更早的系統(tǒng)上沒有問題),也可能是URL Loading System內(nèi)部類之間的依賴所致辞色。
        [super storeCachedResponse:cachedResponse forRequest:request];
    }
    
    return [super cachedResponseForRequest:request];
    
}

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request
{
    
    NSLog(@"+++++request2 = %@ === +++response2 = %@",request,cachedResponse);
    
    [super storeCachedResponse:cachedResponse forRequest:request];
    
}

@end

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
   NSLog(@"-----默認緩存-------%lu", (unsigned long)[NSURLCache sharedURLCache].memoryCapacity);
    NSLog(@"------默認緩存-----%lu", (unsigned long)[NSURLCache sharedURLCache].diskCapacity);
    
    MOBUrlCache *cache = [[MOBUrlCache alloc] initWithMemoryCapacity:(2*1024*1024) diskCapacity:((100 * 1024 * 1024)) diskPath:nil];
    [MOBUrlCache setSharedURLCache:cache];
    
    NSLog(@"------設(shè)置緩存---->>>---%lu", (unsigned long)cache.diskCapacity);
    NSLog(@"------設(shè)置緩存---->>>---%lu", (unsigned long)cache.memoryCapacity);
    
}


2018-09-26 20:59:35.195512+0800 MOBFrameWorkTest[35355:1380494] -----默認緩存-------512000
2018-09-26 20:59:35.195650+0800 MOBFrameWorkTest[35355:1380494] ------默認緩存-----10000000
2018-09-26 20:59:35.196919+0800 MOBFrameWorkTest[35355:1380494] ------設(shè)置緩存---->>>---104857600
2018-09-26 20:59:35.197012+0800 MOBFrameWorkTest[35355:1380494] ------設(shè)置緩存---->>>---2097152
  

PS:NSURLCache緩存不緩存request及response骨宠,不是由request得緩存策略決定的,緩存策略只是說明是否加載已經(jīng)存在的緩存相满,緩存機制有URL Loading System提供层亿。

參考鏈接:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13
http://petersteinberger.com/blog/2012/nsurlcache-uses-a-disk-cache-as-of-ios5/
https://nshipster.cn/nsurlcache/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市立美,隨后出現(xiàn)的幾起案子棕所,更是在濱河造成了極大的恐慌,老刑警劉巖悯辙,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異迎吵,居然都是意外死亡躲撰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門击费,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拢蛋,“玉大人,你說我怎么就攤上這事蔫巩∽焕猓” “怎么了快压?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垃瞧。 經(jīng)常有香客問我蔫劣,道長,這世上最難降的妖魔是什么个从? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任脉幢,我火速辦了婚禮,結(jié)果婚禮上嗦锐,老公的妹妹穿的比我還像新娘嫌松。我一直安慰自己,他們只是感情好奕污,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布萎羔。 她就那樣靜靜地躺著,像睡著了一般碳默。 火紅的嫁衣襯著肌膚如雪贾陷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天腻窒,我揣著相機與錄音昵宇,去河邊找鬼。 笑死儿子,一個胖子當著我的面吹牛瓦哎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柔逼,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼蒋譬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了愉适?” 一聲冷哼從身側(cè)響起犯助,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎维咸,沒想到半個月后剂买,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡癌蓖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年瞬哼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片租副。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡坐慰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出用僧,到底是詐尸還是另有隱情结胀,我是刑警寧澤赞咙,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站糟港,受9級特大地震影響攀操,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜着逐,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一崔赌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耸别,春花似錦健芭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至省有,卻和暖如春痒留,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蠢沿。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工伸头, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舷蟀。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓恤磷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親野宜。 傳聞我的和親對象是個殘疾皇子扫步,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

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