前言
現(xiàn)如今的移動(dòng)應(yīng)用開(kāi)發(fā),網(wǎng)絡(luò)模塊幾乎成了標(biāo)配。如果你是早期 iOS 開(kāi)發(fā)者的話(huà),那么你對(duì) NSURLConnection
一定不會(huì)陌生。但其操作起來(lái)有許多不便,這也使得大家更愿意使用第三方庫(kù)的解決方案,比如大名鼎鼎的 AFNetworking 你一定有所耳聞。正是因?yàn)檫@一點(diǎn),蘋(píng)果隨著 iOS 7 的發(fā)布,也為開(kāi)發(fā)者帶來(lái)了改進(jìn)后的原生網(wǎng)絡(luò)庫(kù)支持,那就是 NSURLSession。
今天,就讓我來(lái)給你道一道從 NSURLConnection
到 NSURLSession
那些你知道和不知道的事。
NSURLConnection
NSURLConnection
作為 Core Foundation / CFNetwork 框架的 API 之上的一個(gè)抽象,在 2003 年,隨著第一版的 Safari 的發(fā)布就發(fā)布了。NSURLConnection
這個(gè)名字,實(shí)際上是指代的 Foundation 框架的 URL 加載系統(tǒng)中一系列相關(guān)聯(lián)的組件:NSURLRequest
、NSURLResponse
、NSURLProtocol
、 NSURLCache
、 NSHTTPCookieStorage
、NSURLCredentialStorage
以及同名類(lèi) NSURLConnection
。
NSURLRequest
被傳遞給 NSURLConnection
。被委托對(duì)象(遵守以前的非正式協(xié)議 <NSURLConnectionDelegate>
和 <NSURLConnectionDataDelegate>
)異步地返回一個(gè) NSURLResponse
以及包含服務(wù)器返回信息的 NSData
。
在一個(gè)請(qǐng)求被發(fā)送到服務(wù)器之前,系統(tǒng)會(huì)先查詢(xún)共享的緩存信息,然后根據(jù)策略(policy)以及可用性(availability)的不同,一個(gè)已經(jīng)被緩存的響應(yīng)可能會(huì)被立即返回。如果沒(méi)有緩存的響應(yīng)可用,則這個(gè)請(qǐng)求將根據(jù)我們指定的策略來(lái)緩存它的響應(yīng)以便將來(lái)的請(qǐng)求可以使用。
在把請(qǐng)求發(fā)送給服務(wù)器的過(guò)程中,服務(wù)器可能會(huì)發(fā)出鑒權(quán)查詢(xún)(authentication challenge),這可以由共享的 cookie 或機(jī)密存儲(chǔ)(credential storage)來(lái)自動(dòng)響應(yīng),或者由被委托對(duì)象來(lái)響應(yīng)。發(fā)送中的請(qǐng)求也可以被注冊(cè)的 NSURLProtocol
對(duì)象所攔截,以便在必要的時(shí)候無(wú)縫地改變其加載行為。
使用步驟
概覽
NSURL
創(chuàng)建一個(gè) NSURL
對(duì)象,設(shè)置請(qǐng)求路徑:
NSURL *url = [NSURL URLWithString:@"協(xié)議://主機(jī)地址/路徑?參數(shù)&參數(shù)"];
解釋如下:
- 協(xié)議:不同的協(xié)議,代表著不同的資源查找方式、資源傳輸方式,比如常用的 HTTP、FTP 等
- 主機(jī)地址:存放資源的主機(jī)的 IP 地址(域名)
- 路徑:資源在主機(jī)中的具體位置
- 參數(shù):參數(shù)可有可無(wú),也可以多個(gè)。如果帶參數(shù)的話(huà),用 “?” 號(hào)后面接參數(shù),多個(gè)參數(shù)的話(huà)之間用 “&” 隔開(kāi)
NSURLRequest
創(chuàng)建一個(gè) NSURLRequest
對(duì)象并傳入 NSURL
,設(shè)置請(qǐng)求頭和請(qǐng)求體:
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
參數(shù)解釋如下:
- requestWithURL:資源路徑
- cachePolicy:緩存策略(無(wú)論使用哪種緩存策略,都會(huì)在本地緩存數(shù)據(jù)),類(lèi)型為枚舉類(lèi)型,取值如下:
- NSURLRequestUseProtocolCachePolicy = 0 // 默認(rèn)的緩存策略,使用協(xié)議的緩存策略
- NSURLRequestReloadIgnoringLocalCacheData = 1 // 每次都從網(wǎng)絡(luò)加載
- NSURLRequestReturnCacheDataElseLoad = 2 // 返回緩存否則加載,很少使用
- NSURLRequestReturnCacheDataDontLoad = 3 // 只返回緩存,沒(méi)有也不加載,很少使用
- timeoutInterval:超時(shí)時(shí)長(zhǎng),默認(rèn) 60s
另外,還可以設(shè)置其它一些信息,比如請(qǐng)求頭,請(qǐng)求體等等,如下:
// 告訴服務(wù)器數(shù)據(jù)為 JSON 類(lèi)型
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
// 設(shè)置請(qǐng)求體(JSON類(lèi)型)
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{@"userid":@"123456"} options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
注意,上面的 request 應(yīng)為 NSMutableURLRequest
,即可變類(lèi)型。(使用 POST 請(qǐng)求的話(huà),那么就必須的使用 NSMutableURLRequest
)
NSURLConnection
使用 NSURLConnection
發(fā)送請(qǐng)求,通過(guò)返回 NSURLResponse
實(shí)例和 NSError
實(shí)例分析結(jié)果,接受服務(wù)器返回?cái)?shù)據(jù)。
NSURLConnection
默認(rèn)的請(qǐng)求類(lèi)型為 GET,下面分異步和同步介紹 GET 的使用(POST 會(huì)在 NSURLSession
中介紹)。
異步請(qǐng)求
異步是指:在發(fā)送請(qǐng)求之后,一邊在子線(xiàn)程中接收返回?cái)?shù)據(jù),一邊執(zhí)行之后的代碼,當(dāng)返回?cái)?shù)據(jù)接收完畢后,采用回調(diào)的方式通知主線(xiàn)程做處理。同時(shí),異步請(qǐng)求有兩種實(shí)現(xiàn)方式。
1.使用 block:
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// 有的時(shí)候,服務(wù)器訪問(wèn)正常,但是會(huì)沒(méi)有數(shù)據(jù)
// 以下的 if 是比較標(biāo)準(zhǔn)的錯(cuò)誤處理代碼
if (connectionError != nil || data == nil) {
//給用戶(hù)的提示信息
NSLog(@"網(wǎng)絡(luò)不給力");
return;
}
}];
參數(shù)說(shuō)明如下:
- completionHandler:請(qǐng)求響應(yīng)后(或者請(qǐng)求超時(shí))執(zhí)行的代碼,queue 為代碼添加到的隊(duì)列,即 block執(zhí)行的線(xiàn)程
- NSURLResponse 為服務(wù)器的響應(yīng),真實(shí)類(lèi)型為 NSHTTPURLResponse,通常只在「下載」功能時(shí),才會(huì)使用;下面是協(xié)議頭的參數(shù):
- URL:響應(yīng)的 URL,有的時(shí)候,訪問(wèn)一個(gè) URL 地址,服務(wù)器可能會(huì)出現(xiàn)重定向,會(huì)定位到新的地址
- MIMEType(Content-Type):服務(wù)器告訴客戶(hù)端,可以用什么軟件打開(kāi)二進(jìn)制數(shù)據(jù)。網(wǎng)絡(luò)之所以豐富多采,是因?yàn)橛胸S富的客戶(hù)端軟件。栗子:Windows 上提示安裝 Flash 插件
- expectedContentLength:預(yù)期的內(nèi)容長(zhǎng)度,要下載的文件長(zhǎng)度,下載文件時(shí)非常有用
- suggestedFilename:「建議」的文件名,方便用戶(hù)直接保存,很多時(shí)候,用戶(hù)并不關(guān)心要保存成什么名字
- textEncodingName:文本的編碼名稱(chēng) @”UTF8”,大多數(shù)都是 UTF8
- statusCode:狀態(tài)碼,在做下載操作的時(shí)候,需要判斷一下
- allHeaderFields:所有的響應(yīng)頭字典時(shí)候,用戶(hù)并不關(guān)心要保存成什么名字
- NSURLResponse 為服務(wù)器的響應(yīng),真實(shí)類(lèi)型為 NSHTTPURLResponse,通常只在「下載」功能時(shí),才會(huì)使用;下面是協(xié)議頭的參數(shù):
- NSData:服務(wù)器返回的數(shù)據(jù),例如:JSON、XML
- NSError:網(wǎng)絡(luò)訪問(wèn)錯(cuò)誤碼
2.使用代理(Delegate):
@interface ViewController ()<NSURLConnectionDataDelegate>
@end
NSURL * url = [NSURL URLWithString:@"http://itangqi.me"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
使用代理可以監(jiān)測(cè)下載過(guò)程:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// 開(kāi)始接收數(shù)據(jù)
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// 正在接收數(shù)據(jù)(會(huì)調(diào)用多次)
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// 接收數(shù)據(jù)失敗
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
// 接收數(shù)據(jù)完成(成功|失敗)
}
同步
同步是指:數(shù)據(jù)的請(qǐng)求在主線(xiàn)程來(lái)執(zhí)行,一旦發(fā)送同步請(qǐng)求,直至服務(wù)器返回?cái)?shù)據(jù)完成,才可以進(jìn)行下一步操作,而網(wǎng)絡(luò)數(shù)據(jù)加載需要一個(gè)時(shí)間過(guò)程,這樣的話(huà)就會(huì)堵塞主線(xiàn)程,當(dāng)然這種使用場(chǎng)景很少。
// 同步請(qǐng)求,代碼會(huì)阻塞在這里一直等待服務(wù)器返回,如果 data 為 nil 則請(qǐng)求失敗,當(dāng)獲取少量數(shù)據(jù)時(shí)可以使用此方法。
// request 參數(shù)同上。
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSURLSession
不管怎樣,NSURLConnection
作為網(wǎng)絡(luò)基礎(chǔ)架構(gòu),已經(jīng)服務(wù)了成千上萬(wàn)的 iOS 和 Mac OS 程序,并且做的還算相當(dāng)不錯(cuò)。但是這些年,一些用例——尤其是在 iPhone 和 iPad 上面——已經(jīng)對(duì) NSURLConnection
的幾個(gè)核心概念提出了挑戰(zhàn),讓蘋(píng)果有理由對(duì)它進(jìn)行重構(gòu)。
在 2013 的 WWDC 上,蘋(píng)果推出了 NSURLConnection
的繼任者:NSURLSession
。
和 NSURLConnection
一樣,NSURLSession
指的也不僅是同名類(lèi) NSURLSession
,還包括一系列相互關(guān)聯(lián)的類(lèi)。NSURLSession
包括了與之前相同的組件,NSURLRequest
與 NSURLCache
,但是把 NSURLConnection
替換成了 NSURLSession
、NSURLSessionConfiguration
以及 NSURLSessionTask
的 3 個(gè)子類(lèi):NSURLSessionDataTask
,NSURLSessionUploadTask
,NSURLSessionDownloadTask
。
與 NSURLConnection
相比,NSURLsession
最直接的改進(jìn)就是可以配置每個(gè) session 的緩存,協(xié)議,cookie,以及證書(shū)策略(credential policy),甚至跨程序共享這些信息。這將允許程序和網(wǎng)絡(luò)基礎(chǔ)框架之間相互獨(dú)立,不會(huì)發(fā)生干擾。每個(gè) NSURLSession
對(duì)象都由一個(gè) NSURLSessionConfiguration
對(duì)象來(lái)進(jìn)行初始化,后者指定了剛才提到的那些策略以及一些用來(lái)增強(qiáng)移動(dòng)設(shè)備上性能的新選項(xiàng)。
NSURLSession
中另一大塊就是 session task。它負(fù)責(zé)處理數(shù)據(jù)的加載以及文件和數(shù)據(jù)在客戶(hù)端與服務(wù)端之間的上傳和下載。NSURLSessionTask
與 NSURLConnection
最大的相似之處在于它也負(fù)責(zé)數(shù)據(jù)的加載,最大的不同之處在于所有的 task 共享其創(chuàng)造者 NSURLSession
這一公共委托者(common delegate)。
我們先來(lái)深入探討 task,過(guò)后再來(lái)討論 NSURLSessionConfiguration
。
概覽
Sessions
// 使用靜態(tài)的 sharedSession 的方法,該類(lèi)使用共享的 seesion,該 seesion 使用全局的 Cache,Cookie 和證書(shū)
+ (NSURLSession *)sharedSession;
// 根據(jù) NSURLSessionConfiguration 創(chuàng)建對(duì)應(yīng)配置的 seesion
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
// 也是根據(jù) NSURLSessionConfiguration 創(chuàng)建對(duì)應(yīng)配置的 seesion,并且可以指定 seesion 的委托和委托所處的隊(duì)列
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
NSURLSessionTask
NSURLSession
本身是不會(huì)進(jìn)行請(qǐng)求的,而是通過(guò)創(chuàng)建 task 的形式進(jìn)行網(wǎng)絡(luò)請(qǐng)求,同一個(gè) NSURLSession 可以創(chuàng)建多個(gè) task,并且這些 task 之間的 cache 和 cookie 是共享的。那么我們就來(lái)看看 NSURLSession 都能創(chuàng)建哪些 task 吧。
Task 可以翻譯為任務(wù),那么在和網(wǎng)絡(luò)請(qǐng)求相關(guān)的任務(wù)中,我們可以理解為:數(shù)據(jù)請(qǐng)求任務(wù)、下載任務(wù)、上傳任務(wù)等。
其實(shí)從類(lèi)名就能猜出它們各自的用途:
NSURLSessionDataTask
:使用這個(gè) task 來(lái)調(diào)用 HTTP GET 方式請(qǐng)求,從服務(wù)器獲取數(shù)據(jù)到內(nèi)存。返回的數(shù)據(jù)格式是 NSData,可根據(jù)需要自行轉(zhuǎn)換成 XML、JSON 等數(shù)據(jù)格式NSURLSessionUploadTask
:使用這個(gè) task 來(lái)上傳磁盤(pán)文件到 web 服務(wù)器,典型地通過(guò) HTTP POST 或者 PUT 方式NSURLSessionDownloadTask
:使用這個(gè) task 來(lái)從遠(yuǎn)程服務(wù)器下載文件到臨時(shí)文件地址NSURLSessionStreamTask
:使用這個(gè) task 來(lái)執(zhí)行異步的讀和寫(xiě)
你也能暫停,恢復(fù)(開(kāi)始)和取消 tasks。
NSURLSessionDataTask POST
/**
* 簡(jiǎn)單 Post 請(qǐng)求,POST 和 GET 請(qǐng)求在于對(duì) request 的處理不同,其余和 GET 相同
*/
- (void)postWithSharedSession {
// 獲取默認(rèn) Session
NSURLSession *session = [NSURLSession sharedSession];
// 創(chuàng)建 URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
// 創(chuàng)建 request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 請(qǐng)求方法
request.HTTPMethod = @"POST";
// 請(qǐng)求體
request.HTTPBody = [@"username=1234&pwd=4321" dataUsingEncoding:NSUTF8StringEncoding];
// 創(chuàng)建任務(wù) task
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 獲取數(shù)據(jù)后解析并輸出
NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
// 啟動(dòng)任務(wù)
[task resume];
}
NSURLSessionDataDelegate 代理方法
NSURLSession
提供了 block 的方式處理返回的數(shù)據(jù),但是如果我們想要在接收數(shù)據(jù)的過(guò)程中處理數(shù)據(jù),我們可以使用 NSURLSessionDataDelegate
的代理方法,分為接收響應(yīng)、接收數(shù)據(jù)和請(qǐng)求完成三個(gè)階段。
- (void)sessionDataDelegate {
// 創(chuàng)建帶有代理方法的自定義 session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 創(chuàng)建任務(wù)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=1234&pwd=4321"]]];
// 啟動(dòng)任務(wù)
[task resume];
}
// 1. 接受到服務(wù)器的響應(yīng)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
NSLog(@"任務(wù)完成");
// 必須設(shè)置對(duì)響應(yīng)進(jìn)行允許處理才會(huì)執(zhí)行后面兩個(gè)操作。
completionHandler(NSURLSessionResponseAllow);
}
// 2. 接收到服務(wù)器的數(shù)據(jù)(可能調(diào)用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 處理每次接收的數(shù)據(jù)
NSLog(@"%s",__func__);
}
// 3. 請(qǐng)求成功或者失敗(如果失敗,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 請(qǐng)求完成,成功或者失敗的處理
NSLog(@"SessionTask %s",__func__);
}
NSURLSessionConfiguration
NSURLSessionConfiguration
對(duì)象用于對(duì) NSURLSession
對(duì)象進(jìn)行初始化。從指定可用網(wǎng)絡(luò),到 cookie,安全性,緩存策略,再到使用自定義協(xié)議,啟動(dòng)事件的設(shè)置,以及用于移動(dòng)設(shè)備優(yōu)化的幾個(gè)新屬性,你會(huì)發(fā)現(xiàn)使用 NSURLSessionConfiguration
可以找到幾乎任何你想要進(jìn)行配置的選項(xiàng)。其有三個(gè)類(lèi)工廠方法:
默認(rèn)模式(+defaultSessionConfiguration):返回一個(gè)標(biāo)準(zhǔn)的 configuration,工作模式類(lèi)似于 NSURLConnection,可以使用緩存的 Cache,Cookie,證書(shū)(credential)
及時(shí)模式(+ephemeralSessionConfiguration): 臨時(shí) session 配置,與默認(rèn)配置相比,這個(gè)配置不會(huì)使用緩存的 Cache,Cookie,證書(shū),只會(huì)存在內(nèi)存里,所以當(dāng)程序退出時(shí),所有的數(shù)據(jù)都會(huì)消失,這對(duì)于實(shí)現(xiàn)像秘密瀏覽這種功能來(lái)說(shuō)是很理想的
后臺(tái)模式(+backgroundSessionConfiguration):它會(huì)創(chuàng)建一個(gè)后臺(tái) session。后臺(tái) session 不同于常規(guī)的,普通的 session,它甚至可以在應(yīng)用程序掛起,退出或者崩潰的情況下運(yùn)行上傳和下載任務(wù)。初始化時(shí)指定的標(biāo)識(shí)符,被用于向任何可能在進(jìn)程外恢復(fù)后臺(tái)傳輸?shù)氖刈o(hù)進(jìn)程(daemon)提供上下文
除了這三種預(yù)設(shè)的模式之外 NSURLSessionConfiguration
還可以進(jìn)行很多的配置。 timeoutIntervalForRequest
和 timeoutIntervalForResource
可以控制網(wǎng)絡(luò)操作的超時(shí)時(shí)間。 allowsCellularAccess
屬性可以控制是否允許使用無(wú)線(xiàn)網(wǎng)絡(luò)。HTTPAdditionalHeaders
可以指定 HTTP 請(qǐng)求頭。
NSURLSessionConfiguration
幾乎可以完成網(wǎng)絡(luò)操作的大多數(shù)配置功能,并且這些配置都綁定到當(dāng)前的 Session 中,我們一旦用配置好的 NSURLSessionConfiguration
初始化 NSURLSession
實(shí)例后,就不能修改這個(gè) NSURLSession
相關(guān)的配置了。所以,一切的配置操作都放在初始化 NSURLSession
之前。
iOS 9
在 iOS 7 后,蘋(píng)果推薦使用
NSURLSession
,并在 iOS 9 中廢棄了NSURLConnection
iOS 9 所有網(wǎng)絡(luò)連接默認(rèn)為 HTTPS 服務(wù),為此推出 App Transport Security,詳情可參見(jiàn):Networking with NSURLSession
NSURLConnection vs. NSURLSession
后臺(tái)上傳和下載:只需在創(chuàng)建
NSURLSession
的時(shí)候配置一個(gè)選項(xiàng),就能得到后臺(tái)網(wǎng)絡(luò)的所有好處。這樣可以延長(zhǎng)電池壽命,并且還支持 UIKit 的多 task,在進(jìn)程間使用相同的委托模型能夠暫停和恢復(fù)網(wǎng)絡(luò)操作:使用 NSURLSession API 能夠暫停,停止,恢復(fù)所有的網(wǎng)絡(luò)任務(wù),再也完全不需要子類(lèi)化 NSOperation
可配置的容器:對(duì)于
NSURLSession
里面的 requests 來(lái)說(shuō),每個(gè)NSURLSession
都是可配置的容器。舉個(gè)例來(lái)說(shuō),假如你需要設(shè)置 HTTP header 選項(xiàng),你只用做一次,session 里面的每個(gè) request 就會(huì)有同樣的配置提高認(rèn)證處理:認(rèn)證是在一個(gè)指定的連接基礎(chǔ)上完成的。在使用
NSURLConnection
時(shí),如果發(fā)出一個(gè)訪問(wèn),會(huì)返回一個(gè)任意的 request。此時(shí),你就不能確切的知道哪個(gè) request 收到了訪問(wèn)。而在 NSURLSession 中,就能用代理處理認(rèn)證豐富的代理模式:在處理認(rèn)證的時(shí)候,
NSURLConnection
有一些基于異步的 block 方法,但是它的代理方法就不能處理認(rèn)證,不管請(qǐng)求是成功或是失敗。在NSURLSession
中,可以混合使用代理和 block 方法處理認(rèn)證上傳和下載通過(guò)文件系統(tǒng):它鼓勵(lì)將數(shù)據(jù)(文件內(nèi)容)從元數(shù)據(jù)(URL 和 settings)中分離出來(lái)
要點(diǎn)
請(qǐng)求的緩存策略使用
NSURLRequestReloadIgnoringCacheData
,忽略本地緩存服務(wù)器響應(yīng)結(jié)束后,要記錄 Etag,服務(wù)器內(nèi)容和本地緩存對(duì)比是否變化的重要依據(jù)
在發(fā)送請(qǐng)求時(shí),設(shè)置 If-None-Match,并且傳入 etag
連接結(jié)束后,要判斷響應(yīng)頭的狀態(tài)碼,如果是 304,說(shuō)明本地緩存內(nèi)容沒(méi)有發(fā)生變化,此時(shí)可以使用本地緩存來(lái)加載數(shù)據(jù),如果緩存文件被意外刪除,程序依然運(yùn)行但會(huì)報(bào)錯(cuò),加載數(shù)據(jù)也失敗
GET 緩存的數(shù)據(jù)會(huì)保存在 Cache 目錄中 /bundleId 下,Cache.db 中
本文轉(zhuǎn)自:wakice的博客
其它資料
NSURLSession與NSURLConnection區(qū)別
NSURLSession與NSURLConnection區(qū)別
兩者的區(qū)別主要在以下幾個(gè)方面:
1. 使用現(xiàn)狀
NSURLSession是NSURLConnection 的替代者,在2013年蘋(píng)果全球開(kāi)發(fā)者大會(huì)(WWDC2013)隨ios7一起發(fā)布,是對(duì)NSURLConnection進(jìn)行了重構(gòu)優(yōu)化后的新的網(wǎng)絡(luò)訪問(wèn)接口。從iOS9.0開(kāi)始, NSURLConnection中發(fā)送請(qǐng)求的兩個(gè)方法已過(guò)期(同步請(qǐng)求,異步請(qǐng)求),初始化網(wǎng)絡(luò)連接(initWithRequest: delegate:)的方法也被設(shè)置為過(guò)期,系統(tǒng)不再推薦使用,建議使用NSURLSession發(fā)送網(wǎng)絡(luò)請(qǐng)求。
2. 普通任務(wù)和上傳
NSURLSession針對(duì)下載/上傳等復(fù)雜的網(wǎng)絡(luò)操作提供了專(zhuān)門(mén)的解決方案,針對(duì)普通、上傳和下載分別對(duì)應(yīng)三種不同的網(wǎng)絡(luò)請(qǐng)求任務(wù):NSURLSessionDataTask, NSURLSessionUploadTask和NSURLSessionDownloadTask.。創(chuàng)建的task都是掛起狀態(tài),需要resume才能執(zhí)行。
當(dāng)服務(wù)器返回的數(shù)據(jù)較小時(shí),NSURLSession與NSURLConnection執(zhí)行普通任務(wù)的操作步驟沒(méi)有區(qū)別。
執(zhí)行上傳任務(wù)時(shí),NSURLSession與NSURLConnection一樣同樣需要設(shè)置POST請(qǐng)求的請(qǐng)求體進(jìn)行上傳。
3. 下載任務(wù)方式
NSURLConnection下載文件時(shí),先將整個(gè)文件下載到內(nèi)存,然后再寫(xiě)入沙盒,如果文件比較大,就會(huì)出現(xiàn)內(nèi)存暴漲的情況。而使用NSURLSessionUploadTask下載文件,會(huì)默認(rèn)下載到沙盒中的tem文件夾中,不會(huì)出現(xiàn)內(nèi)存暴漲的情況,但在下載完成后會(huì)將tem中的臨時(shí)文件刪除,需要在初始化任務(wù)方法時(shí),在completionHandler回調(diào)中增加保存文件的代碼。
以下代碼是實(shí)例化網(wǎng)絡(luò)下載任務(wù)時(shí)將下載的文件保存到沙盒的caches文件夾中:
[NSURLSessionDownloadTask [NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:@"http://127.0.0.1/dawenjian.zip"] completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//獲取沙盒的caches路徑
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:@"kkk.dmg"];
//生成URL路徑
NSURL *DCurl = [NSURL fileURLWithPath:path];
//將文件保存到指定文件目錄下
[[NSFileManager defaultManager]moveItemAtURL:location toURL:DCurl error:nil]; }]resume];
4. 請(qǐng)求方法的控制
NSURLConnection實(shí)例化對(duì)象,實(shí)例化開(kāi)始,默認(rèn)請(qǐng)求就發(fā)送(同步發(fā)送),不需要調(diào)用start方法。而cancel 可以停止請(qǐng)求的發(fā)送,停止后不能繼續(xù)訪問(wèn),需要?jiǎng)?chuàng)建新的請(qǐng)求。
NSURLSession有三個(gè)控制方法,取消(cancel),暫停(suspend),繼續(xù)(resume),暫停后可以通過(guò)繼續(xù)恢復(fù)當(dāng)前的請(qǐng)求任務(wù)。
5. 斷點(diǎn)續(xù)傳的方式
NSURLConnection進(jìn)行斷點(diǎn)下載,通過(guò)設(shè)置訪問(wèn)請(qǐng)求的HTTPHeaderField的Range屬性,開(kāi)啟運(yùn)行循環(huán),NSURLConnection的代理方法作為運(yùn)行循環(huán)的事件源,接收到下載數(shù)據(jù)時(shí)代理方法就會(huì)持續(xù)調(diào)用,并使用NSOutputStream管道流進(jìn)行數(shù)據(jù)保存。
NSURLSession進(jìn)行斷點(diǎn)下載,當(dāng)暫停下載任務(wù)后,如果 downloadTask (下載任務(wù))為非空,調(diào)用 cancelByProducingResumeData:(void (^)(NSData *resumeData))*completionHandler*
這個(gè)方法,這個(gè)方法接收一個(gè)參數(shù),完成處理代碼塊,這個(gè)代碼塊有一個(gè) NSData 參數(shù) resumeData,如果 resumeData 非空,我們就保存這個(gè)對(duì)象到視圖控制器的 resumeData 屬性中。在點(diǎn)擊再次下載時(shí),通過(guò)調(diào)用 [ [self.session downloadTaskWithResumeData:self.resumeData]resume]方法進(jìn)行繼續(xù)下載操作。
經(jīng)過(guò)以上比較可以發(fā)現(xiàn),使用NSURLSession進(jìn)行斷點(diǎn)下載更加便捷。
6. 配置信息
NSURLSession的構(gòu)造方法(sessionWithConfiguration:delegate:delegateQueue)中有一個(gè) NSURLSessionConfiguration類(lèi)的參數(shù)可以設(shè)置配置信息,其決定了cookie,安全和高速緩存策略,最大主機(jī)連接數(shù),資源管理,網(wǎng)絡(luò)超時(shí)等配置。NSURLConnection不能進(jìn)行這個(gè)配置,相比于 NSURLConnection 依賴(lài)于一個(gè)全局的配置對(duì)象,缺乏靈活性而言,NSURLSession 有很大的改進(jìn)了。
NSURLSession可以設(shè)置三種配置信息,分別通過(guò)調(diào)用三個(gè)累方法返回配置對(duì)象:
+ (NSURLSessionConfiguration *)defaultSessionConfiguration,配置信息使用基于硬盤(pán)的持久話(huà)Cache,保存用戶(hù)的證書(shū)到鑰匙串,使用共享cookie存儲(chǔ);
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration ,配置信息和default大致相同。除了,不會(huì)把cache,證書(shū),或者任何和Session相關(guān)的數(shù)據(jù)存儲(chǔ)到硬盤(pán),而是存儲(chǔ)在內(nèi)存中,生命周期和Session一致。比如瀏覽器無(wú)痕瀏覽等功能就可以基于這個(gè)來(lái)做;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier,配置信息可以創(chuàng)建一個(gè)可以在后臺(tái)甚至APP已經(jīng)關(guān)閉的時(shí)候仍然在傳輸數(shù)據(jù)的session。注意,后臺(tái)Session一定要在創(chuàng)建的時(shí)候賦予一個(gè)唯一的identifier,這樣在APP下次運(yùn)行的時(shí)候,能夠根據(jù)identifier來(lái)進(jìn)行相關(guān)的區(qū)分。如果用戶(hù)關(guān)閉了APP,IOS 系統(tǒng)會(huì)關(guān)閉所有的background Session。而且,被用戶(hù)強(qiáng)制關(guān)閉了以后,IOS系統(tǒng)不會(huì)主動(dòng)喚醒APP,只有用戶(hù)下次啟動(dòng)了APP,數(shù)據(jù)傳輸才會(huì)繼續(xù)。