看了很多文章,大部分都是說緩存怎么讀取隧膏,而緩存的寫入都說是系統(tǒng)自動(dòng)寫入哗讥,說的不清楚。下面我就自己看過的文章和自己的實(shí)踐理解胞枕,寫一篇關(guān)于NSURLRequest杆煞、NSURLSession和AFNetworking緩存的介紹。
學(xué)習(xí)iOS的緩存主要有兩個(gè)方面腐泻,數(shù)據(jù)寫入
與緩存讀取
决乎,下面先講緩存讀取
緩存讀取
只需要設(shè)置request請求的cachePolicy屬性,就可以控制緩存的讀取派桩,很簡單构诚。
- 常用緩存策略
//使用協(xié)議緩存策略,也就是按照響應(yīng)頭的HTTP緩存字段來使用緩存
NSURLRequestUseProtocolCachePolicy
//忽略緩存
NSURLRequestReloadIgnoringLocalCacheData
//有緩存則讀緩存(就算緩存過期也讀取)铆惑,沒有則網(wǎng)絡(luò)加載
NSURLRequestReturnCacheDataElseLoad
//有緩存則讀緩存(就算緩存過期也讀取)范嘱,沒有則報(bào)錯(cuò)
NSURLRequestReturnCacheDataDontLoad
- 下面有三種方法配置request的緩存策略,而且生效優(yōu)先級依次降低
1员魏、設(shè)置單個(gè)請求的緩存策略丑蛤,直接設(shè)置單個(gè)請求的緩存策略,其生效的優(yōu)先級是最高的撕阎,不會(huì)受到全局緩存配置的影響受裹。
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;//本次請求忽略本地緩存 重新請求數(shù)據(jù)
2、設(shè)置NSURLSession緩存策略虏束,使用該session發(fā)出的所有請求都會(huì)服從該session的緩存策略名斟,其生效優(yōu)先級低于上面的單個(gè)請求配置
NSURLSessionConfiguration *confi = [NSURLSessionConfiguration defaultSessionConfiguration];
confi.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
NSURLSession *session = [NSURLSession sessionWithConfiguration:confi delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
3、設(shè)置AFNetworking緩存策略魄眉,生效優(yōu)先級低于上面的NSURLSession緩存配置
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
注意點(diǎn):如果允許使用緩存砰盐,在沒有網(wǎng)絡(luò)的情況下,倘若本地有緩存則會(huì)走成功的回調(diào)返回緩存數(shù)據(jù)坑律,不會(huì)走請求失敗的回調(diào)
緩存寫入
緩存的寫入所需的條件比較多
- 系統(tǒng)默認(rèn)使用NSURLCache做緩存容器岩梳,首先我們可以按需設(shè)置NSURLCache的大小囊骤,我這里設(shè)置了4兆的內(nèi)存20兆的磁盤空間做緩存。如果不需要緩存那么直接設(shè)置空間為0.
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:URLCache]; //設(shè)置共享緩存容器
- 緩存在寫入前會(huì)調(diào)用NSURLSession的
willCacheResponse
這個(gè)代理方法冀值,并且只有將response傳給completionHandler()
之后系統(tǒng)才會(huì)緩存該響應(yīng)也物,如果傳入nil
則表示不緩存該響應(yīng)。我們可以在這個(gè)方法里修改響應(yīng)信息列疗,然后保存修改之后的響應(yīng)信息滑蚯。(不只是GET請求,POST請求也能觸發(fā)代理方法抵栈,保存響應(yīng)) - 響應(yīng)信息會(huì)被緩存下來的先決條件:首先要滿足
willCacheResponse
代理方法的觸發(fā)條件(如果這個(gè)代理方法都不能觸發(fā)告材,那么緩存的寫入也無從談起)。然后將需要緩存的響應(yīng)傳給代理方法的completionHandler()
進(jìn)行保存古劲,如果傳入nil
則表示不緩存斥赋。
注意:如果設(shè)置共享緩存空間為0,那么也不會(huì)緩存響應(yīng)信息
只有滿足以下所有條件時(shí)才會(huì)觸發(fā)
willCacheResponse
代理方法:
1产艾、請求是針對HTTP或HTTPS URL(或你自己的支持緩存的自定義網(wǎng)絡(luò)協(xié)議)疤剑。
2、請求成功(狀態(tài)碼在200-299范圍內(nèi))闷堡。
3隘膘、返回的響應(yīng)數(shù)據(jù)來自服務(wù)器,而不是來自本地緩存杠览。(廢話弯菊,本地緩存不會(huì)再緩存一遍)
下面兩個(gè)條件是我從其他文章看到的,經(jīng)過測試發(fā)現(xiàn)并不需要
//4倦零、session會(huì)話配置的緩存策略允許緩存。(這個(gè)條件好像有問題吨悍,會(huì)話配置只決定是否使用緩存扫茅,而不決定緩存的寫入)
//5、NSURLRequest對象的緩存策略(如果適用)允許緩存育瓜。(這也是控制響應(yīng)的寫入)
6葫隙、服務(wù)器響應(yīng)中的緩存相關(guān)頭(如果存在)允許緩存。也就是響應(yīng)頭中的HTTP緩存字段(這里一下解釋不清躏仇,請自行查看http的緩存相關(guān)字段)
7恋脚、響應(yīng)大小足夠小,可以合理地放入緩存中焰手。 (例如糟描,如果您提供磁盤緩存曹货,則響應(yīng)不得超過磁盤緩存大小的5%熏迹。)
注:如果代理實(shí)現(xiàn)此方法willCacheResponse,則該代理方法中必須調(diào)用completionHandler完成處理程序吻育,否則應(yīng)用程序會(huì)泄漏內(nèi)存。
- 對willCacheResponse這個(gè)代理方法的一些使用
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
//這里可以修改HTTP的緩存字段
NSURLResponse *response = proposedResponse.response;
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse*)response;
NSDictionary *headers = HTTPResponse.allHeaderFields;
NSCachedURLResponse *cachedResponse;
//修改Cache-Control字段的值
NSMutableDictionary *modifiedHeaders = headers.mutableCopy;
[modifiedHeaders setObject:@"max-age=10" forKey:@"Cache-Control"];
NSHTTPURLResponse * modifiedResponse;
modifiedResponse = [[NSHTTPURLResponse alloc] initWithURL:HTTPResponse.URL
statusCode:HTTPResponse.statusCode HTTPVersion:@"HTTP/1.1"
headerFields:modifiedHeaders];
cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:modifiedResponse
data:proposedResponse.data
userInfo:proposedResponse.userInfo
storagePolicy:proposedResponse.storagePolicy];
//將修改后的響應(yīng)信息交給該block執(zhí)行见间,該block就會(huì)保存該響應(yīng)聊闯,當(dāng)然了也可以不修改響應(yīng)
completionHandler(cachedResponse); //返回nil則表示不緩存響應(yīng)
}
AFN緩存
AFN默認(rèn)已經(jīng)實(shí)現(xiàn)了willCacheResponse代理方法,只要滿足這個(gè)代理方法的觸發(fā)條件米诉,那么該響應(yīng)就會(huì)被緩存到本地菱蔬。
并且如果在緩存響應(yīng)之前想要修改這個(gè)響應(yīng),可以設(shè)置AFN的一個(gè)block史侣,在這個(gè)block中修改響應(yīng)信息拴泌,如下。(當(dāng)然也可以在這個(gè)block中設(shè)置過濾抵窒,根據(jù)請求信息過濾掉不想緩存的響應(yīng))
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager setDataTaskWillCacheResponseBlock:^NSCachedURLResponse * _Nonnull(NSURLSession * _Nonnull session,
NSURLSessionDataTask * _Nonnull dataTask, NSCachedURLResponse * _Nonnull proposedResponse) {
//修改響應(yīng)信息
NSURLResponse *response = proposedResponse.response;
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse*)response;
NSDictionary *headers = HTTPResponse.allHeaderFields;
NSCachedURLResponse *cachedResponse;
//這里就可以針對Cache-Control進(jìn)行更改弛针,然后直接我們通過某方法獲取NSCachedURLResponse的時(shí)候就可以先去判斷下頭域信息
NSMutableDictionary *modifiedHeaders = headers.mutableCopy;
[modifiedHeaders setObject:@"max-age=1000" forKey:@"Cache-Control"];
NSHTTPURLResponse *modifiedResponse = [[NSHTTPURLResponse alloc] initWithURL:HTTPResponse.URL statusCode:
HTTPResponse.statusCode HTTPVersion:@"HTTP/1.1" headerFields:modifiedHeaders];
cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:modifiedResponse data:proposedResponse.data
userInfo:proposedResponse.userInfo storagePolicy:proposedResponse.storagePolicy];
return cachedResponse; //返回修改后的響應(yīng)
}];
如果想要AFN使用已經(jīng)保存好的響應(yīng),那么直接設(shè)置AFN的緩存策略允許使用緩存即可李皇,幾種緩存使用策略上面已經(jīng)介紹過了
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
通常在實(shí)際應(yīng)用中需要根據(jù)不同的網(wǎng)絡(luò)狀態(tài)設(shè)置不同的緩存策略削茁,一般對于JSON數(shù)據(jù)來說,因?yàn)樗ǔJ欠琴Y源文件掉房,可能會(huì)經(jīng)常變動(dòng)茧跋,所以在有網(wǎng)的情況下需要禁用緩存來實(shí)時(shí)刷新,在無網(wǎng)的情況下才使用緩存數(shù)據(jù)卓囚。通绸迹可以配合AFN的網(wǎng)絡(luò)監(jiān)聽一起使用,以達(dá)到在不同的網(wǎng)絡(luò)環(huán)境下使用不同的緩存策略的效果哪亿。
//使用AFN框架來檢測網(wǎng)絡(luò)狀態(tài)的改變
-(void)AFNReachability{
/*
AFNetworkReachabilityStatusUnknown = 未知
AFNetworkReachabilityStatusNotReachable = 沒有網(wǎng)絡(luò)
AFNetworkReachabilityStatusReachableViaWWAN = 3G
AFNetworkReachabilityStatusReachableViaWiFi = WIFI
*/
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusUnknown:
self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
break;
case AFNetworkReachabilityStatusNotReachable:
self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
self.manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
self.manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
break;
default:
self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
break;
}
}];
//開始監(jiān)聽
[manager startMonitoring];
}
GET粥烁、POST使用緩存時(shí)的區(qū)別
系統(tǒng)緩存都是存放在以下路徑的數(shù)據(jù)庫文件中,注意顯示隱藏文件蝇棉,因?yàn)槲募A的名字是AppID讨阻,有些AppID是以"."點(diǎn)開頭的,系統(tǒng)會(huì)將以點(diǎn)開頭的文件當(dāng)做隱藏文件
數(shù)據(jù)庫文件中就存放著我們的緩存數(shù)據(jù)篡殷,并且以請求的URL為鍵值進(jìn)行保存钝吮,讀取緩存時(shí)也是系統(tǒng)自動(dòng)通過URL讀取。當(dāng)請求沒有參數(shù)或者參數(shù)值固定不變時(shí)板辽,GET和POST請求都能夠正確讀到緩存奇瘦,但是當(dāng)請求有參數(shù),并且參數(shù)的值不固定時(shí)劲弦,POST使用緩存就有問題了耳标,你會(huì)發(fā)現(xiàn)無論參數(shù)值如何變化,獲取到的緩存數(shù)據(jù)都是一樣的邑跪。這是因?yàn)镻OST請求的參數(shù)沒有拼接在URL后面麻捻,所以導(dǎo)致參數(shù)值無論怎么變纲仍,URL始終是一樣的,以URL為鍵值獲取的緩存數(shù)據(jù)也是一樣(因?yàn)閁RL一樣贸毕,所以系統(tǒng)每次請求都會(huì)覆蓋上一次緩存)郑叠。而GET請求不一樣,他的參數(shù)拼接在URL后面明棍,所以當(dāng)參數(shù)值變化時(shí)URL也隨之改變乡革,不同的參數(shù)值唯一確定不同的一個(gè)URL,所以讀取的緩存也就一一對應(yīng)摊腋,不會(huì)重復(fù)沸版。
總結(jié)一下:
- 請求沒有參數(shù)或者參數(shù)值固定不變,那么GET和POST都可以正常使用緩存
- 請求有參數(shù)且參數(shù)值會(huì)變化兴蒸,那么GET可以正常使用緩存视粮,POST不可以
參考文章
iOS網(wǎng)絡(luò)——NSURLCache設(shè)置網(wǎng)絡(luò)請求緩存
NSURLSession發(fā)送請求通過NSURLCache 做的緩存
NSURLSession 所有的都在這里(二)
https://www.cnblogs.com/rainySue/p/huan-cun.html#toc_11