iOS源碼解析—AFNetworking(URLSession)

概述

上一篇主要分析了基于NSURLConnection的AFURLConnectionOperation,本篇主要分析一下基于NSURLSession方式的相關代碼,主要分為核心類AFURLSessionManager及其子類AFHTTPSessionManager。

初始化

AFHTTPSessionManager封裝了HTTP請求的幾個方法,例如GET、POST等請求,首先通過初始化方法創建AFHTTPSessionManager對象。

初始化過程最終執行到-(instance)initWithBaseURL: sessionConfiguration:方法中,下面是代碼注釋:

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    self.baseURL = url; //設置baseURL,作為構建url的前綴
    self.requestSerializer = [AFHTTPRequestSerializer serializer];//序列化類
    self.responseSerializer = [AFJSONResponseSerializer serializer];//反序列化類
    return self;
}

首先調用父類對象的initWithSessionConfiguration:方法,初始化一些基本參數。下面是部分代碼注釋:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    if (!configuration) { //創建默認的NSURLSessionConfiguration對象
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    self.sessionConfiguration = configuration;
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1; //最大并發數為1,同步隊列

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; //創建session對象

    self.responseSerializer = [AFJSONResponseSerializer serializer]; //JSON序列化解析類
    self.securityPolicy = [AFSecurityPolicy defaultPolicy]; //默認安全策略類

#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; //網絡狀態監聽類
#endif
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    self.lock = [[NSLock alloc] init]; //線程鎖
    self.lock.name = AFURLSessionManagerLockName;
    ...
    return self;
}

首先創建一個NSURLSessionConfiguration對象,該對象有三種創建方式,采用默認的方式創建,該對象是屬于會話配置項,作用它創建的所有NSURLSession對象。然后創建根據sessionConfiguration對象創建一個session對象,指定delegate和delegate執行的queue,默認是同步隊列。然后創建JSON反序列化對象和安全策略對象,網絡狀態監聽對象。mutableTaskDelegatesKeyedByTaskIdentifier字典保存sessionTask和sessionTaskDelegate的關系。同時設置線程鎖類控制多線程的數據同步。

接下來在子類AFHTTPSessionManager的方法中設置baseURL,可以作為后續構建Request對象url的前綴,然后設置序列化和反序列化的工具類。

NSURLSessionTask

AFHTTPSessionManager提供了一系列HTTP請求的方法,例如GET、POST、HEAD、DELETE等方法,這些方法的流程相似,都是先根據session對象創建一個task對象,然后調用resume方法啟動task。task對象有三種類型,分別是NSURLSessionDataTask、NSURLSessionUploadTask和NSURLSessionDownloadTask,分別如下:

NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    //創建request對象,序列化請求報文
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
           //創建失敗,拋給上層
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }
        return nil;
    }
    __block NSURLSessionDataTask *dataTask = nil;
    //創建task
    dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];
    return dataTask;
}

首先構建http請求的報文,包括請求頭、請求體等信息,創建request對象,然后調用dataTaskWithRequest: completionHandler:方法創建NSURLSessionDataTask對象,方法如下:

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionDataTask *dataTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        dataTask = [self.session dataTaskWithRequest:request]; //創建task對象
    });
    [self addDelegateForDataTask:dataTask completionHandler:completionHandler]; //創建delegate,綁定completionHandler回調,關聯delegate
    return dataTask;
}

首先在串行隊列中創建一個task對象,使用dispatch_sync保證線程阻塞直到創建完成,為什么不直接在主線程做不太清楚,然后調用addDelegateForDataTask方法為task對象添加delegate對象,并綁定completionHandler回調block,用于http請求完成后執行。addDelegateForDataTask方法注釋如下:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //創建delegate對象
    delegate.manager = self;
    delegate.completionHandler = completionHandler; //綁定completionHandler
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask]; //關聯task個delegate
}

首先創建一個delegate對象,綁定completionHandler,然后調用setDelegate:forTask:方法關聯task和delegate,代碼注釋如下,

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; //存入字典
    [delegate setupProgressForTask:task]; //delegate監聽task的進度
    [self addNotificationObserverForTask:task]; //添加通知
    [self.lock unlock];
}

通過mutableTaskDelegatesKeyedByTaskIdentifier字典維護關系,key是task的taskIdentifier,value是delegate,通過setupProgressForTask方法使delegate監聽task的進度。

NSURLSessionUploadTask

當需要上傳一個數據到服務端,需要創建NSURLSessionUploadTask,創建方式有三種:

uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
uploadTask = [self.session uploadTaskWithStreamedRequest:request];

支持從url地址、內存、流中讀取數據,創建task的流程和NSURLSessionDataTask類似,創建完成后需要為其添加一個delegate對象并綁定,如下:

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromData:(NSData *)bodyData
                                         progress:(NSProgress * __autoreleasing *)progress
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionUploadTask *uploadTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; //創建一個uploadTask對象
    });
    [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; //添加delegate
    return uploadTask;
}

addDelegateForUploadTask:progress:completionHandler:方法為uploadTask設置delegate,代碼注釋如下:

- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
                        progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
               completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //創建delegate對象
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:uploadTask]; //綁定delegate和uploadTask對象
    delegate.uploadProgressBlock = uploadProgressBlock; //設置上傳進度回調blcok
}

首先創建delegate對象,然后綁定delegate和uploadTask對象,最后設置uploadProgressBlock,用于通知外界上傳數據的進度。

NSURLSessionDownloadTask

當需要下載數據并寫入指定地址時,需要創建NSURLSessionDownloadTask對象:

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(NSProgress * __autoreleasing *)progress
                                          destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    __block NSURLSessionDownloadTask *downloadTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        downloadTask = [self.session downloadTaskWithRequest:request]; //創建一個NSURLSessionDownloadTask
    });
    [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; //創建delegate,綁定
    return downloadTask;
}

其中destination是一個block,返回一個url,表示需要寫入數據的地址。addDelegateForDownloadTask方法和upload的類似,代碼注釋如下:

- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
                          progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                       destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                 completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //創建delegate對象
    delegate.manager = self;
    delegate.completionHandler = completionHandler; //綁定completionHandler
    if (destination) {
        //設置block,返回寫入下載數據的地址
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
            return destination(location, task.response);
        };
    }
    downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:downloadTask]; //綁定delegate和downloadTask對象
    delegate.downloadProgressBlock = downloadProgressBlock; //設置下載進度回調blcok
}

AFURLSessionManagerTaskDelegate

AFURLSessionManagerTaskDelegate是代理對象,主要作用有兩個,一是監聽task的進度,并回拋給上層,二是處理task的回調方法,delegate和task是一一對應關系。下面是類的定義:

@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData; //響應數據
@property (nonatomic, strong) NSProgress *uploadProgress; //上傳進度
@property (nonatomic, strong) NSProgress *downloadProgress; //下載進度
@property (nonatomic, copy) NSURL *downloadFileURL; //下載地址
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; //下載完成回調
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; //上傳進度block
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; //下載進度blcok
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; //請求完成block
@end

mutableData負責接收和拼裝server響應的數據,uploadProgress和downloadProgress負責管理發送和接收數據的進度,uploadProgressBlock和downloadProgressBlock負責將進度回拋給上層。completionHandler是請求完成時調用的blcok,負責將響應數據回拋給上層。downloadFileURL是存儲下載數據的本地地址,通過執行downloadTaskDidFinishDownloading回調得到。

  1. 監聽進度

    調用setupProgressForTask:方法關聯task的進度屬性,注釋如下:

    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
     //1.task需要傳輸的所有數據大小
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
         //2.關聯progress和task的狀態  
         [self.uploadProgress setCancellable:YES];
        [self.uploadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.uploadProgress setPausable:YES];
        [self.uploadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
        if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.uploadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
        [self.downloadProgress setCancellable:YES];
        [self.downloadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.downloadProgress setPausable:YES];
        [self.downloadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
        if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.downloadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
     //3.監聽task的數據傳輸狀態
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self          forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
     //4.監聽progress的fractionCompleted屬性值
        [self.downloadProgress addObserver:self                        forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                   options:NSKeyValueObservingOptionNew
                                   context:NULL];
        [self.uploadProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                 options:NSKeyValueObservingOptionNew
                                 context:NULL];
    }
    

    該方法首先獲取當前task需要發送或者接受的數據總大小。然后關聯progress和task的狀態,當progress暫停時,相應的task需要suspend,當progress取消時,task會cancel,當progress啟動時,task會resume。然后通過kvo的方式監聽task的進度屬性,countOfBytesReceived和countOfBytesSent表示當前的接受和發送的數據量,countOfBytesExpectedToReceive和countOfBytesExpectedToSend表示需要接受和發送的數據總量。同時監聽progress的fractionCompleted屬性,fractionCompleted屬性表示任務的整體進度,值從0到1,kvo的響應方法注釋如下:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        if ([object isKindOfClass:[NSURLSessionTask class]]) {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
                self.downloadProgress.completedUnitCount = [change[@"new"] longLongValue]; //當前接受的數據量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
                self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue]; //需要接受的數據總量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
                self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue]; //當前發送的數據量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
                self.uploadProgress.totalUnitCount = [change[@"new"] longLongValue]; //需要發送的數據總量
            }
        }
        else if ([object isEqual:self.downloadProgress]) {
            if (self.downloadProgressBlock) {
                self.downloadProgressBlock(object); //進度回拋給上層
            }
        }
        else if ([object isEqual:self.uploadProgress]) {
            if (self.uploadProgressBlock) {
                self.uploadProgressBlock(object); //進度回拋給上層
            }
        }
    }
    

    該方法根據監聽到的屬性,實時更新progress的進度屬性,然后將屬性回拋給上層。delegate調用cleanUpProgressForTask注銷kvo。

  2. 處理URLSessionTask回調

    在初始化的方法時,通過[NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]將AFURLSessionManager對象設置為session的delegate,通過URLSession創建出來的每個task,在執行的過程中,即網絡請求的過程中,都會調用AFURLSessionManager對象來處理一系列代理方法。然后AFURLSessionManager對象進一步調用AFURLSessionManagerTaskDelegate對象來處理,具體實現在下文分析。

AFURLSessionManager

AFURLSessionManager是處理網絡請求回調的核心類,主要負責處理NSURLSession以及NSURLSessionTask的各種回調邏輯。下面分析一下主要的delegate方法:

NSURLSessionDelegate
  1. -(void)URLSession: didReceiveChallenge: completionHandler方法

    發HTTPS請求時,在SSL握手過程中,服務端將證書下發客戶端,客戶端需要校驗服務端證書,在AF代碼中,核心代碼注釋如下:

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { //如果需要驗證trust對象
     if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { //用securityPolicy對象驗證trust對象
          credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; //驗證通過,根據trust對象生成證書憑證
          if (credential) {
         disposition = NSURLSessionAuthChallengeUseCredential; //使用憑證
         } else {
             disposition = NSURLSessionAuthChallengePerformDefaultHandling; //默認方式
         }
     } else {
         disposition = NSURLSessionAuthChallengeRejectProtectionSpace; //驗證失敗,終止
     }
    } else {
     disposition = NSURLSessionAuthChallengePerformDefaultHandling; //用默認方式
    }
    ...
    if (completionHandler) {
     completionHandler(disposition, credential);
    }
    

    該方法首先判斷是否需要驗證trust對象,trust是一個SecTrustRef類型的對象,用于對服務器端傳來的X.509證書評估,然后用securityPolicy進行驗證。如果驗證通過,則用trust對象生成一個證書憑證,并采用NSURLSessionAuthChallengeUseCredential的方式。如果不能生成憑證,則用默認方式。最終將方式和憑證通過completionHandler的方式回調給系統,進行后續的校驗和連接。

NSURLSessionTaskDelegate
  1. -(void)NSURLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:方法

    該方法在服務端重定向一個url的時候會觸發,處理代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
            newRequest:(NSURLRequest *)request
     completionHandler:(void (^)(NSURLRequest *))completionHandler
    {
        NSURLRequest *redirectRequest = request; //用默認重定向的request
    if (self.taskWillPerformHTTPRedirection) { //如果有自定義block,則用block返回的request對象
     redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }
     if (completionHandler) { //回調給系統
         completionHandler(redirectRequest);
     }
    }
    
  2. -(void)URLSession:task:didReceiveChallenge:completionHandler:方法

    該方法的邏輯和URLSessionDelegate的方法相同,但是task級別的方法,同時在兩個方法都實現的情況下,會優先進入上文方法進行校驗,不進入該方法校驗。

  3. -(void)URLSession:task:needNewBodyStream方法

    如果通過stream的方式上傳數據至服務器,會調用[self.session uploadTaskWithStreamedRequest:request]方法創建NSURLSessionUploadTask,在執行task發送請求時,會觸發該方法,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
     needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
    {
        NSInputStream *inputStream = nil;
        if (self.taskNeedNewBodyStream) { //自定義block處理,返回新的stream
            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);
        }
    }
    
  4. -(void)URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:

    當發送請求報文時,觸發該方法,返回發送的數據量等信息。

    - (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) { //用contentLength替換totalBytesExpectedToSend
                totalUnitCount = (int64_t) [contentLength longLongValue];
            }
        }
     //回調給上層,包括當前一次發送的數據、發送數據的數據總量、需要發送的數據量
        if (self.taskDidSendBodyData) {
            self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
        }
    }
    

    bytesSent表示當前一次發送的數據量,totalBytesSent表示已經發送的數據總量,totalUnitCount表示本次請求需要發送的數據總量。

  5. -(void)URLSession:task:didCompleteWithError:

    當前task完成時,會觸發該回調方法,NSURLSessionDataTask、NSURLSessionDownloadTask、NSURLSessionUploadTask結束時都會調用該方法,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; 
        if (delegate) { //調用delegate處理回調
            [delegate URLSession:session task:task didCompleteWithError:error];
            [self removeDelegateForTask:task]; //刪除task對應的delegate
        }
        if (self.taskDidComplete) { //如果有回調block,執行block
            self.taskDidComplete(session, task, error);
        }
    }
    

    首先通過task的taskIdentifier找到對應的delegate,然后調用delegate的方法處理,最后再刪除delegate,如果有回調block,執行block。下面是delegate對象方法的注釋:

    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
     ...
        //data是響應報文數據
        NSData *data = nil;
        if (self.mutableData) {
            data = [self.mutableData copy];
            //We no longer need the reference, so nil it out to gain back some memory.
            self.mutableData = nil;
        }
     ...
        if (error) { //如果請求失敗
            userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
    
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) { //在指定隊列或者主隊列中將error回調給上層
                    self.completionHandler(task.response, responseObject, error);
                }
                ...
                });
            });
        } else {
            dispatch_async(url_session_manager_processing_queue(), ^{
                NSError *serializationError = nil;
                 //反序列化響應報文數據
                responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
    
                if (self.downloadFileURL) {
                     //如果是downloadTask,responseObject是downloadFileURL
                    responseObject = self.downloadFileURL;
                }
                 ...
                dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                    if (self.completionHandler) {
                        self.completionHandler(task.response, responseObject, serializationError); //在指定隊列或者主隊列中將反序列后的對象responseObject回調給上層
                    }
                });
                 ...
            });
        }
    #pragma clang diagnostic pop
    }
    

    該方法主要是獲取到服務端響應報文數據后,用responseSerializer反序列化數據,并回調給上層,當創建一個NSURLSessionDataTask類型的task并執行時,會得到報文數據mutableData。如果創建NSURLSessionDownloadTask執行,直接下載數據到本地,mutableData為nil。反序列化的過程在異步隊列中執行,提高了性能。

NSURLSessionDataDelegate

使用NSURLSessionDataTask發HTTP請求時,在請求的過程中,會觸發以下代理方法:

  1. -(void)URLSession:dataTask:didReceiveResponse:completionHandler:方法

    當開始接收到服務器的響應時,該方法被調用,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
     completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
    {
       //默認處理方式
        NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
        if (self.dataTaskDidReceiveResponse) { //如果實現block,通過block修改處理方式
            disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
        }
        if (completionHandler) { //回調給系統
            completionHandler(disposition);
        }
    }
    

    當接收到服務器響應時,說明網絡請求已經連接,默認是NSURLSessionResponseAllow表示允許task繼續執行,進行通信和數據傳輸,如果外部設置了dataTaskDidReceiveResponse的block,則可以修改通過block修改處理方式。其他的處理方式有:

    NSURLSessionResponseCancel //取消task

    NSURLSessionResponseAllow //允許task繼續執行

    NSURLSessionResponseBecomeDownload //dataTask變成downloadTask

    NSURLSessionResponseBecomeStream //變成NSURLSessionStreamTask

  2. -(void)URLSession:dataTask:didBecomeDownloadTask:

    上一個方法的disposition設置為NSURLSessionResponseBecomeDownload時,dataTask變成了downloadTask,會觸發該方法,需要對將之前delegate關聯到新的task。代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        if (delegate) { //重新關聯delegate和task
            [self removeDelegateForTask:dataTask];
            [self setDelegate:delegate forTask:downloadTask];
        }
        if (self.dataTaskDidBecomeDownloadTask) {
            self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
        }
    }
    
  3. -(void)URLSession:dataTask:didReceiveData:

    當接收服務端響應后并開始傳輸數據時,該方法一次或者多次被出發,返回服務端響應的報文數據,在該方法中調用delegate拼接報文數據。

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
    
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        //delegate處理data數據
        [delegate URLSession:session dataTask:dataTask didReceiveData:data];
    
        if (self.dataTaskDidReceiveData) {
            self.dataTaskDidReceiveData(session, dataTask, data);
        }
    }
    

    AFURLSessionManagerTaskDelegate對象負責處理響應報文數據data:

    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        [self.mutableData appendData:data]; //拼接到mutableData中
    }
    

    delegate將data拼接到mutableData中。

  4. -(void)URLSession:dataTask:willCacheResponse:completionHandler:方法

    當構建一個request對象時,默認的緩存屬性策略cachePolicy是NSURLRequestUseProtocolCachePolicy,即根據服務端響應報文的header中的緩存策略決定是否緩存數據,當需要緩存數據時,會觸發該方法,將響應報文數據緩存到本地,實現該方法可以修改需要緩存的數據。

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
     willCacheResponse:(NSCachedURLResponse *)proposedResponse
     completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
    {
        NSCachedURLResponse *cachedResponse = proposedResponse;
    
        if (self.dataTaskWillCacheResponse) { //如果實現block,通過block修改緩存數據
            cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
        }
        if (completionHandler) { //回調給系統,存儲緩存數據
            completionHandler(cachedResponse);
        }
    }
    
NSURLSessionDownloadDelegate
  1. URLSession:downloadTask:didFinishDownloadingToURL:

    執行NSURLSessionDownloadTask下載數據到本地,當下載完成時,會觸發該方法,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
        if (self.downloadTaskDidFinishDownloading) { //如果實現block,執行下面邏輯
            NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); //獲取需要寫入數據的地址fileURL
            if (fileURL) {
                delegate.downloadFileURL = fileURL;
                NSError *error = nil;
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; //將寫入location地址的數據移至fileURL
                if (error) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
                }
                return;
            }
        }
        if (delegate) { //通過delegate處理下載完成的邏輯
            [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
        }
    }
    

    如果實現了downloadTaskDidFinishDownloading的block,執行block提供寫入下載數據的地址fileURL,因為之前系統默認將下載數據寫入location地址,會將location的數據移至fileURL。如果沒有實現block,默認調用delegate的方法處理邏輯,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        ...
        if (self.downloadTaskDidFinishDownloading) {
            self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); //獲取下載地址downloadFileURL
            if (self.downloadFileURL) {//將location中的數據移至downloadFileURL
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
            ...  
            }
        }
    }
    

    該方法執行downloadTaskDidFinishDownloading獲取需要寫入下載數據的地址fileURL,在之前的addDelegateForDownloadTask方法中設置了downloadTaskDidFinishDownloading。然后將默認location地址中的下載數據移動至fileURL。

  2. URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:

    執行NSURLSessionDownloadTask下載數據的過程中,會觸發該方法通知下載的進度,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {
        if (self.downloadTaskDidWriteData) { //將進度數據回調給外界
            self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
        }
    }
    

    bytesWritten是本次接收到的數據,totalBytesWritten是已經接受到數據字節數,totalBytesExpectedToWrite是需要下載的總數據量。

總結

AFURLSessionManager實現了NSURLSession、NSURLSessionTask及其子類的各種代理回調方法,同時提供了各種自定義block,增強了代碼邏輯的靈活性,同時可以監聽數據傳輸的進度,通知外界。該模塊對于網絡請求的邏輯封裝的比較好,相關代碼值得學習。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容