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/