一個(gè) NSURLSession 對(duì)象代表了一個(gè) session(會(huì)話),這個(gè) session 可以類(lèi)比為瀏覽器的一個(gè) tab 或者負(fù)責(zé)某一類(lèi)請(qǐng)求例如后臺(tái)下載的環(huán)境皿淋,它創(chuàng)建并管理著一組 NSURLSessionTask谬盐,每個(gè)NSURLSessionTask
對(duì)象則對(duì)應(yīng)了一個(gè)特定的請(qǐng)求程剥。
當(dāng)創(chuàng)建一個(gè)NSURLSession
對(duì)象時(shí)变过,需要一個(gè)提供配置信息的
NSURLSessionConfiguration 對(duì)象,這個(gè)配置信息對(duì)象定義了同一域名的最大連接數(shù)症脂、移動(dòng)網(wǎng)絡(luò)下是否可請(qǐng)求谚赎、請(qǐng)求的緩存和 cookie 保存策略等行為淫僻,session 中的所有 task 共用這個(gè)配置。
注意區(qū)別于 HTTP 協(xié)議中的 session 概念壶唤。HTTP 本身是無(wú)狀態(tài)的協(xié)議雳灵,請(qǐng)求之間是沒(méi)有聯(lián)系的,所以 web 應(yīng)用為了保存用戶登錄狀態(tài)闸盔,返回用戶信息悯辙,就需要用到會(huì)話技術(shù)。HTTP 會(huì)話的原理是迎吵,當(dāng)需要開(kāi)啟一個(gè)會(huì)話時(shí)躲撰,服務(wù)器端會(huì)生成一個(gè) session ID 并保存到客戶端的 cookie 中,之后客戶端的請(qǐng)求帶有的 cookie 里就有了其對(duì)應(yīng)會(huì)話的 session ID击费,服務(wù)器通過(guò)這個(gè) session ID 就可以去獲取到保存在服務(wù)器端的對(duì)應(yīng)的會(huì)話信息拢蛋,并做相應(yīng)處理了。session 的信息可以保存在服務(wù)器的臨時(shí)文件中蔫巩,也可以用 redis 來(lái)保存 session 信息谆棱。
創(chuàng)建 session
-
sharedSession
,共享的單例 session 對(duì)象批幌,不需要通過(guò) configuration 和 delegate 來(lái)創(chuàng)建础锐,只提供獲取資源到內(nèi)存中的支持。因?yàn)?session 對(duì)象的 delegate 屬性為只讀荧缘,所以無(wú)法設(shè)置sharedSession
的delegate。shared session 使用了共享的
NSURLCache
拦宣、NSHTTPCookieStorage
和NSURLCredentialStorage
對(duì)象截粗。 -
- sessionWithConfiguration:
,根據(jù)不同的NSURLSessionConfiguration
對(duì)象(default鸵隧、ephemeral 或 background)創(chuàng)建 session 對(duì)象绸罗。 -
- sessionWithConfiguration:delegate:delegateQueue:
,根據(jù) configuration 對(duì)象創(chuàng)建 session 的同時(shí)豆瘫,設(shè)置 session 對(duì)象的 delegate 和 delegate queue珊蟀。
session 相關(guān)屬性
-
configuration
,session 的 configuration 對(duì)象的 copy外驱,修改這個(gè) configuration 對(duì)象并不會(huì)影響 session育灸,可用于創(chuàng)建新的 session。 -
delegate
昵宇,創(chuàng)建 session 時(shí)傳入的 delegate磅崭,session 對(duì)delegate 強(qiáng)引用,只有當(dāng) session 執(zhí)行 invalidate 相關(guān)方法后瓦哎,才會(huì)釋放引用砸喻。 -
delegateQueue
柔逼,創(chuàng)建 session 時(shí)傳入的 delegateQueue,所有的代理方法和 block 回調(diào)都會(huì)在這個(gè) queue 中執(zhí)行割岛。 -
sessionDescription
愉适,可給 session 設(shè)置一個(gè)描述,用于調(diào)試或界面顯示癣漆。
創(chuàng)建 task
根據(jù)請(qǐng)求的不同需求维咸,session 可以創(chuàng)建 data task、download task扑媚、upload task 和 stream task腰湾。
// data task
- dataTaskWithURL:
- dataTaskWithURL:completionHandler:
- dataTaskWithRequest:
- dataTaskWithRequest:completionHandler:
// download task
- downloadTaskWithURL:
- downloadTaskWithURL:completionHandler:
- downloadTaskWithRequest:
- downloadTaskWithRequest:completionHandler:
- downloadTaskWithResumeData:
- downloadTaskWithResumeData:completionHandler:
// upload task
- uploadTaskWithRequest:fromData:
- uploadTaskWithRequest:fromData:completionHandler:
- uploadTaskWithRequest:fromFile:
- uploadTaskWithRequest:fromFile:completionHandler:
- uploadTaskWithStreamedRequest:
// session task
- streamTaskWithHostName:port:
- streamTaskWithNetService:
如果傳入了 completionHandler 參數(shù),那么 delegate 中返回?cái)?shù)據(jù)疆股、返回響應(yīng)费坊、請(qǐng)求完成等方法都不會(huì)再調(diào)用,但其他 delegate 方法還是會(huì)調(diào)用旬痹,如證書(shū)鑒定等附井。
調(diào)用 completionHandler 會(huì)傳入3個(gè)參數(shù),第一個(gè)參數(shù)是返回的數(shù)據(jù)两残,data task 和 upload task 為NSData
類(lèi)型永毅,download task 為臨時(shí)文件保存路徑的NSURL
對(duì)象,該文件在回調(diào)執(zhí)行完后會(huì)被刪除人弓,所以要在回調(diào)中做相應(yīng)的持久化處理沼死。后兩個(gè)參數(shù)是NSURLResponce
類(lèi)型的 response 和NSError
類(lèi)型的 error。
對(duì)于 HTTP 協(xié)議來(lái)說(shuō)崔赌,無(wú)論服務(wù)器返回的響應(yīng) code 是成功還是失敗意蛀,以 task 的角度看,這個(gè)請(qǐng)求 task 是成功的健芭,data 中會(huì)包含返回的內(nèi)容县钥,response 代表了響應(yīng)的元信息,error 則為 nil慈迈,如果發(fā)生客戶端相關(guān)錯(cuò)誤導(dǎo)致 task 請(qǐng)求失敗若贮,error 中則會(huì)包含相關(guān)信息。如果創(chuàng)建 task 時(shí)傳入的是NSURLRequest
對(duì)象而不是NSURL
對(duì)象痒留,那么 task 除了請(qǐng)求 request 中的 URL 指定的資源谴麦,相關(guān)的請(qǐng)求行為,如請(qǐng)求方法(GET or POST)狭瞎、緩存策略细移、超時(shí)時(shí)間、請(qǐng)求體內(nèi)容等熊锭,也都會(huì)根據(jù) request 對(duì)象設(shè)置弧轧,而這可能會(huì)覆蓋 configuration 中指定的某些行為雪侥。
另外 download task 還可以根據(jù)下載中斷時(shí)的 resume data 創(chuàng)建。調(diào)用 download task 的- cancelByProducingResumeData:
方法會(huì)在回調(diào)中返回 resume data精绎,另一種可能獲得 resume data 的情況是速缨,在 download task 失敗時(shí),返回的 error 的 userInfo 里 keyNSURLSessionDownloadTaskResumeData
對(duì)應(yīng)保存了 resume data代乃。
而 upload task 的創(chuàng)建必須傳入NSURLRequest
對(duì)象旬牲,上傳的內(nèi)容可以通過(guò)參數(shù)傳入NSData
對(duì)象或代表文件路徑的NSURL
對(duì)象。對(duì)于
- uploadTaskWithStreamedRequest:
方法搁吓,則必須實(shí)現(xiàn)代理方法
URLSession:task:needNewBodyStream:
用以提供上傳內(nèi)容原茅。
管理 session
session 和 task 的實(shí)例可以不需要應(yīng)用來(lái)維護(hù),系統(tǒng)會(huì)維護(hù)創(chuàng)建的 session 實(shí)例堕仔,而 task 實(shí)例由創(chuàng)建它的 session 維護(hù)擂橘,當(dāng)一個(gè) task 執(zhí)行完成,如果沒(méi)有其他引用摩骨,則會(huì)被銷(xiāo)毀通贞,當(dāng) session 調(diào)用了 invalidate 方法,如果沒(méi)有其他引用恼五,則會(huì)被銷(xiāo)毀昌罩。
- finishTasksAndInvalidate
- invalidateAndCancel
當(dāng)對(duì) session invalidate 后,就不能再創(chuàng)建新的 task 了灾馒,兩個(gè)方法的不同之處是茎用,- finishTasksAndInvalidate
會(huì)等到正在執(zhí)行的 task 執(zhí)行完成,調(diào)用完所有回調(diào)或 delegate 后睬罗,釋放對(duì) delegate 的強(qiáng)引用绘搞,而- invalidateAndCancel
方法則是直接取消所有正在執(zhí)行的 task。
- flushWithCompletionHandler:
將內(nèi)存中的 cookie傅物、證書(shū)等寫(xiě)到硬盤(pán),之后的請(qǐng)求會(huì)使用新的 TCP 連接琉预,傳入的 completionHandler 在上述操作完成后執(zhí)行董饰。
- resetWithCompletionHandler:
清空所有的 cookie、緩存圆米、證書(shū)等卒暂,傳入的 completionHandler 在上述操作完成后執(zhí)行。
- (void)getTasksWithCompletionHandler:(void (^)(NSArray<NSURLSessionDataTask *> *dataTasks, NSArray<NSURLSessionUploadTask *> *uploadTasks, NSArray<NSURLSessionDownloadTask *> *downloadTasks))completionHandler;
- (void)getAllTasksWithCompletionHandler:(void (^)(NSArray<__kindof NSURLSessionTask *> *tasks))completionHandler;
獲取 session 中的 task娄帖,在獲取完 task 列表后會(huì)執(zhí)行傳入的 completionHandler 參數(shù)也祠,而 task 列表則作為 block 的參數(shù)傳入。
Delegate
通過(guò) session 的 delegate 可以在請(qǐng)求的整個(gè)生命周期執(zhí)行許多自定義的行為近速。若使用 session 時(shí)不提供 delegate诈嘿,session 對(duì)象則使用一個(gè)系統(tǒng)提供的 delegate堪旧,在這種情況下,創(chuàng)建 task 時(shí)就必須傳入處理回調(diào)的 block 參數(shù)奖亚,否則無(wú)法獲得返回的數(shù)據(jù)了淳梦。
@protocol NSURLSessionDelegate <NSObject>
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error;
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler;
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session;
@end
@protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * __nullable))completionHandler;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream * __nullable bodyStream))completionHandler;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error;
@end
@protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
@optional
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * __nullable cachedResponse))completionHandler;
@end
@protocol NSURLSessionDownloadDelegate <NSURLSessionTaskDelegate>
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
@optional
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes;
@end
@protocol NSURLSessionStreamDelegate <NSURLSessionTaskDelegate>
@optional
- (void)URLSession:(NSURLSession *)session readClosedForStreamTask:(NSURLSessionStreamTask *)streamTask;
- (void)URLSession:(NSURLSession *)session writeClosedForStreamTask:(NSURLSessionStreamTask *)streamTask;
- (void)URLSession:(NSURLSession *)session betterRouteDiscoveredForStreamTask:(NSURLSessionStreamTask *)streamTask;
- (void)URLSession:(NSURLSession *)session streamTask:(NSURLSessionStreamTask *)streamTask didBecomeInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream;
@end
- 當(dāng)在 SSL 握手階段,如果服務(wù)器要求驗(yàn)證客戶端身份或向客戶端提供其證書(shū)用于驗(yàn)證時(shí)昔字,則會(huì)調(diào)用
URLSession:didReceiveChallenge:completionHandler:
或
URLSession:task:didReceiveChallenge:completionHandler:
方法爆袍。傳入的參數(shù)除了對(duì)應(yīng)的 session 和 task,還有NSURLAuthenticationChallenge
對(duì)象作郭,它封裝了服務(wù)端對(duì)客戶端的驗(yàn)證請(qǐng)求陨囊,根據(jù)它的 protectionSpace 屬性的
authenticationMethod,可以知道服務(wù)端要通過(guò)哪種方式驗(yàn)證客戶端或是要求客戶端驗(yàn)證服務(wù)端的證書(shū)夹攒。在代理方法中要執(zhí)行傳入的
completionHandler Block蜘醋,這個(gè)回調(diào)接收兩個(gè)參數(shù):
NSURLSessionAuthChallengeDisposition
類(lèi)型的 disposition,描述了如何處理驗(yàn)證請(qǐng)求芹助,NSURLCredential
對(duì)象堂湖,當(dāng)處理方式需要提供證書(shū)時(shí),傳入該參數(shù)状土。當(dāng) authenticationMethod 的值為:
NSURLAuthenticationMethodNTLM
无蜂、NSURLAuthenticationMethodNegotiate
、
NSURLAuthenticationMethodClientCertificate
和
NSURLAuthenticationMethodServerTrust
時(shí)蒙谓,系統(tǒng)會(huì)先嘗試調(diào)用 session 級(jí)的處理方法斥季,若 session 級(jí)未實(shí)現(xiàn),則嘗試調(diào)用 task 級(jí)的處理方法累驮,而其他情況則是直接調(diào)用 task 級(jí)的處理方法酣倾,無(wú)論 session 級(jí)方法是否實(shí)現(xiàn)。 - 當(dāng)使用
- uploadTaskWithStreamedRequest:
方法創(chuàng)建的 task 發(fā)起請(qǐng)求時(shí)谤专,必須實(shí)現(xiàn)代理方法URLSession:task:needNewBodyStream:
躁锡,以提供請(qǐng)求需要的 stream。因?yàn)閺妮斎肓髯x數(shù)據(jù)是不可逆的置侍,所以在上傳失敗時(shí)映之,可能會(huì)重新調(diào)用該方法讀取流。在方法中執(zhí)行 block completionHandler 參數(shù)蜡坊,向其傳入
NSInputStream
對(duì)象杠输。 - 當(dāng)執(zhí)行 upload task 時(shí),系統(tǒng)會(huì)定期的調(diào)用
URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
方法秕衙,報(bào)告上傳請(qǐng)求體的進(jìn)度蠢甲。 - 當(dāng)響應(yīng)為重定向到其他請(qǐng)求時(shí),系統(tǒng)會(huì)調(diào)用代理方法
URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:
据忘,在方法中要執(zhí)行 block 參數(shù) completionHandler鹦牛,可向其傳入重定向的新的NSURLRequest
對(duì)象搞糕,那么會(huì)正常執(zhí)行重定向請(qǐng)求,也可以傳入 nil能岩,則不執(zhí)行重定向請(qǐng)求并以當(dāng)前響應(yīng)體作為重定向后的響應(yīng)寞宫。 - 當(dāng)執(zhí)行以方法
- downloadTaskWithResumeData:
或
- downloadTaskWithResumeData:completionHandler:
創(chuàng)建的 download task 時(shí),會(huì)調(diào)用代理方法
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
- 當(dāng) data task 收到響應(yīng)時(shí)拉鹃,會(huì)調(diào)用代理方法
URLSession:dataTask:didReceiveResponse:completionHandler:
辈赋,在方法中要執(zhí)行 block 參數(shù) completionHandler,向其傳入
NSURLSessionResponseDisposition
枚舉類(lèi)型參數(shù)膏燕,指明繼續(xù)正常返回響應(yīng)體钥屈,還是取消請(qǐng)求,或者將 task 轉(zhuǎn)變?yōu)?download task坝辫。支持
multipart/x-mixed-replace
時(shí)篷就,傳入NSURLSessionResponseBecomeDownload
,將 task 轉(zhuǎn)換為 download task 后近忙,會(huì)調(diào)用代理方法
URLSession:dataTask:didBecomeDownloadTask:
- 當(dāng)從服務(wù)端接收數(shù)據(jù)時(shí)竭业,系統(tǒng)會(huì)調(diào)用相應(yīng)的代理方法,定期提供已接收到的數(shù)據(jù)及舍,對(duì)于 data task未辆,會(huì)調(diào)用方法
URLSession:dataTask:didReceiveData:
,而 download task 則會(huì)調(diào)用方法
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
- 對(duì)于 data task 或 upload task锯玛,在收到所有數(shù)據(jù)后咐柜,可能會(huì)調(diào)用方法
URLSession:dataTask:willCacheResponse:completionHandler:
,提供給應(yīng)用決定是否緩存響應(yīng)的機(jī)會(huì)攘残。方法中要執(zhí)行 block 參數(shù) completionHandler拙友,如果緩存則傳入一個(gè)NSCachedURLResponse
對(duì)象,若不想緩存可傳入 nil歼郭。當(dāng)返回的響應(yīng)為緩存中的時(shí)候遗契,則不調(diào)用該方法。 - 當(dāng) task 執(zhí)行完成后病曾,會(huì)調(diào)用代理方法
URLSession:task:didCompleteWithError:
姊途,如果 task 失敗,error 參數(shù)為包含具體信息的NSError
對(duì)象知态,若成功,error 則為 nil立叛。對(duì)于 HTTP 協(xié)議來(lái)說(shuō)负敏,服務(wù)器返回的響應(yīng) code 為失敗時(shí),不屬于 task 執(zhí)行失敗的情況秘蛇,以 task 的角度看其做,這個(gè)請(qǐng)求 task 是成功執(zhí)行的顶考,data 中會(huì)包含返回的內(nèi)容。當(dāng) download task 失敗時(shí)妖泄,在 error 對(duì)象的 userInfo 中可以根據(jù) key
NSURLSessionDownloadTaskResumeData
獲得已下載的數(shù)據(jù)驹沿,之后可用于創(chuàng)建從斷點(diǎn)處繼續(xù)下載的 task。當(dāng) download task 成功執(zhí)行完成后蹈胡,還會(huì)執(zhí)行代理方法URLSession:downloadTask:didFinishDownloadingToURL:
渊季,這個(gè)方法是
NSURLSessionDownloadDelegate
協(xié)議中必須實(shí)現(xiàn)的,方法參數(shù)傳入一個(gè)下載文件的臨時(shí)保存路徑罚渐,應(yīng)用需要讀取文件的內(nèi)容或?qū)⑽募频狡渌胤饺春海驗(yàn)榇矸椒▓?zhí)行結(jié)束后,該路徑下的文件會(huì)被刪除荷并,注意合砂,如果讀取文件內(nèi)容的話,則要在單獨(dú)的線程中進(jìn)行源织,否則可能會(huì)造成頁(yè)面卡死翩伪,影響用戶體驗(yàn)。 - 當(dāng)對(duì) session 對(duì)象執(zhí)行了 invalidate 的相關(guān)方法后谈息,會(huì)調(diào)用代理方法
URLSession:didBecomeInvalidWithError:
缘屹。之后 session 會(huì)釋放對(duì)代理對(duì)象的強(qiáng)引用。
拷貝策略
當(dāng)拷貝一個(gè) session 對(duì)象或 task 對(duì)象時(shí)黎茎,并不會(huì)創(chuàng)建新的實(shí)例囊颅,而是返回該對(duì)象本身。
后臺(tái) session
當(dāng)掛起的應(yīng)用執(zhí)行的后臺(tái)傳輸(下載傅瞻、上傳)完成或者發(fā)生錯(cuò)誤以及需要身份驗(yàn)證時(shí)踢代,系統(tǒng)會(huì)運(yùn)行應(yīng)用并調(diào)用應(yīng)用代理的
- application:handleEventsForBackgroundURLSession:completionHandler:
方法。如果傳輸完成可以執(zhí)行一些更新界面的操作嗅骄,如果沒(méi)有成功胳挎,則可以根據(jù) identifier 參數(shù)重新創(chuàng)建 background session 繼續(xù)執(zhí)行。在該方法中應(yīng)使用實(shí)例變量強(qiáng)引用 block 參數(shù) completionHandler溺森,在上述相關(guān)操作(如重新關(guān)聯(lián) session)完成后慕爬,系統(tǒng)會(huì)調(diào)用 session 的代理方法
URLSessionDidFinishEventsForBackgroundURLSession:
,此時(shí)在該代理方法中運(yùn)行之前應(yīng)用代理維持的 block屏积,以通知系統(tǒng)應(yīng)用已完成處理工作医窿,要注意的是,該 completionHandler 要在主線程中執(zhí)行炊林。