版本記錄
| 版本號(hào) | 時(shí)間 |
|---|---|
| V1.0 | 2018.02.28 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請(qǐng)求,都離不開一個(gè)非常有用的框架AFNetworking,可以說(shuō)這個(gè)框架的知名度已經(jīng)超過(guò)了蘋果的底層網(wǎng)絡(luò)請(qǐng)求部分,很多人可能不知道蘋果底層是如何發(fā)起網(wǎng)絡(luò)請(qǐng)求的,但是一定知道
AFNetworking,接下來(lái)幾篇我們就一起詳細(xì)的解析一下這個(gè)框架。感興趣的可以看上面寫的幾篇。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請(qǐng)求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
3. AFNetworking源碼探究(三) —— GET請(qǐng)求實(shí)現(xiàn)之任務(wù)進(jìn)度設(shè)置和通知監(jiān)聽(一)
4. AFNetworking源碼探究(四) —— GET請(qǐng)求實(shí)現(xiàn)之代理轉(zhuǎn)發(fā)思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細(xì)解析(一)
回顧
上一篇主要講了NSURLSessionDelegate中的三個(gè)代理方法,講述了它們的使用場(chǎng)景以及用法,這一篇主要講AFURLSessionManager中代理NSURLSessionTaskDelegate詳細(xì)解析。
1. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
首先看一下系統(tǒng)的API接口
/* An HTTP request is attempting to perform a redirection to a different
* URL. You must invoke the completion routine to allow the
* redirection, allow the redirection with a modified request, or
* pass nil to the completionHandler to cause the body of the redirection
* response to be delivered as the payload of this request. The default
* is to follow redirections.
*
* For tasks in background sessions, redirections will always be followed and this method will not be called.
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler;
下面看一下AFN中該代理方法的實(shí)現(xiàn)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
{
NSURLRequest *redirectRequest = request;
if (self.taskWillPerformHTTPRedirection) {
redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
}
if (completionHandler) {
completionHandler(redirectRequest);
}
}
這里,主要做了下面幾個(gè)邏輯:
- 如果有對(duì)應(yīng)的block
taskWillPerformHTTPRedirection,那么就調(diào)用self.taskWillPerformHTTPRedirection(session, task, response, request),返回一個(gè)新的request。 - 接著就是利用生成的request重新請(qǐng)求
if (completionHandler) {
completionHandler(redirectRequest);
}
- 這個(gè)方法是在服務(wù)器去重定向的時(shí)候,才會(huì)被調(diào)用。
- 此方法只會(huì)在
default session或者ephemeral session中調(diào)用,而在background session中,session task會(huì)自動(dòng)重定向。 - 補(bǔ)充一點(diǎn),初始化
NSURLSession對(duì)象的時(shí)候需要使用NSURLSessionConfiguration。有三個(gè)類工廠方法:-
+ defaultSessionConfiguration返回一個(gè)標(biāo)準(zhǔn)的 configuration,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。 -
+ ephemeralSessionConfiguration返回一個(gè)預(yù)設(shè)配置,這個(gè)配置中不會(huì)對(duì)緩存,Cookie 和證書進(jìn)行持久性的存儲(chǔ)。這對(duì)于實(shí)現(xiàn)像秘密瀏覽這種功能來(lái)說(shuō)是很理想的。 -
+ backgroundSessionConfiguration:(NSString *)identifier的獨(dú)特之處在于,它會(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)提供上下文。
-
2. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
首先看一下蘋果的API接口
/* The task has received a request specific authentication challenge.
* If this delegate is not implemented, the session specific authentication challenge
* will *NOT* be called and the behavior will be the same as using the default handling
* disposition.
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
下面看一下AFN中該方法的實(shí)現(xiàn)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.taskDidReceiveAuthenticationChallenge) {
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
這里我們需要知道:
- 這個(gè)是
non-session-level級(jí)別的認(rèn)證 - 多了一個(gè)參數(shù)
task,然后調(diào)用我們自定義的Block會(huì)多回傳這個(gè)task作為參數(shù),這樣我們就可以根據(jù)每個(gè)task去自定義我們需要的https認(rèn)證方式。
3. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
首先看一下蘋果的API接口
/* Sent if a task requires a new, unopened body stream. This may be
* necessary when authentication has failed for any request that
* involves a body stream.
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler;
看一下AFN對(duì)該方法的實(shí)現(xiàn)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
NSInputStream *inputStream = nil;
if (self.taskNeedNewBodyStream) {
inputStream = self.taskNeedNewBodyStream(session, task);
} else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
inputStream = [task.originalRequest.HTTPBodyStream copy];
}
if (completionHandler) {
completionHandler(inputStream);
}
}
該代理方法會(huì)在下面兩種情況被調(diào)用
- 如果
task是由uploadTaskWithStreamedRequest:創(chuàng)建的,那么提供初始的request body stream時(shí)候會(huì)調(diào)用該代理方法。 - 因?yàn)檎J(rèn)證挑戰(zhàn)或者其他可恢復(fù)的服務(wù)器錯(cuò)誤,而導(dǎo)致需要客戶端重新發(fā)送一個(gè)含有
body stream的request,這時(shí)候會(huì)調(diào)用該代理。
4. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
首先看一下蘋果的API接口
/* Sent periodically to notify the delegate of upload progress. This
* information is also available as properties of the task.
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
看一下AFN對(duì)該方法的實(shí)現(xiàn)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
int64_t totalUnitCount = totalBytesExpectedToSend;
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
if (self.taskDidSendBodyData) {
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
}
}
就是每次發(fā)送數(shù)據(jù)給服務(wù)器,會(huì)回調(diào)這個(gè)方法,通知已經(jīng)發(fā)送了多少。
5. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error;
首先看一下蘋果的API接口
/* Sent as the last message related to a specific task. Error may be
* nil, which implies that no error occurred and this task is complete.
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error;
看一下AFN對(duì)該方法的實(shí)現(xiàn)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
這個(gè)代理就是task完成了的回調(diào),這里涉及到task和代理的綁定與接觸綁定,代碼不難,就不多說(shuō)了。
后記
本篇主要介紹了NSURLSessionTaskDelegate中五個(gè)代理方法的實(shí)現(xiàn)及其使用場(chǎng)景和注意事項(xiàng)。
