AFNetworking源碼解析


logo.png

AFNetworking是目前Apple開發(fā)中使用最廣泛的網(wǎng)絡庫(Swift版本Alamofire);

前言

這篇文章以部分AFNetworking源碼為基礎,解析這個網(wǎng)絡庫的內部組成和部分關鍵技術;通過這篇文章可以加深一點對AFNetworking的整體組成結構、和網(wǎng)絡請求關鍵部分的理解;框架中涉及到的UI擴展和其他部分的內容封裝主要會以說明其作用的方式進行概述,并給出一些詳細講述的文章鏈接方便更具體的了解細節(jié);

正文內容

文章分為以下兩個部分:

  • AFNetworking的組成結構
  • AFNetworking核心部分的實現(xiàn)

1、AFNetworking的組成結構

AFNetworking是一個輕量級的網(wǎng)絡庫,它的組成可以通過下面這張圖來概括:


組成結構.png

可以大致把框架劃分為5個部分,上圖中紅線線框標注的部分為核心部分,下面分別對這幾個部分進行說明;

a、AFHTTPSessionManager

AFHTTPSessionManager繼承自AFURLSessionManager,它的作用主要是封裝了不同的HTTP請求方式(GET、HEAD、POST、PUT、PATCH、DELETE)的便捷方法;我們在實現(xiàn)自己APP的網(wǎng)絡層時一般都是通過生成AFHTTPSessionManager的單例對象進行具體的網(wǎng)絡請求任務的;

AFHTTPSessionManager中不同的請求方法都會通過調用本類中的以下方法生成具體的請求任務,并在返回task后調用task的resume方法開始這個任務:

//AFHTTPSessionManager通過以下方法生成具體的請求任務
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    //根據(jù)請求方法等參數(shù)生成具體的請求URLRequest;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {    //生成請求request出錯時才執(zhí)行;self.completionQueue是任務完成時的回調隊列,如果沒有設置則使用主線程隊列
        if (failure) {
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }
        return nil;
    }

    __block NSURLSessionDataTask *dataTask = nil;
    //通過調用父類AFURLSessionManager的方法生成具體的請求任務,并根據(jù)任務的完成狀態(tài)執(zhí)行對應的回調block
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        //completionHandler會在任務請求結束時回調,請求可能成功或失敗
        if (error) {
            if (failure) {  //執(zhí)行失敗的回調block
                failure(dataTask, error);
            }
        } else {
            if (success) {  //執(zhí)行成功的回調block
                success(dataTask, responseObject);
            }
        }
    }];
    return dataTask;  //返回具體的網(wǎng)絡任務,并會在調用方執(zhí)行任務的resume方法
}

b、核心部分(AFURLSessionManager、AFURLRequestSerialization、AFURLResponseSerialization)

這部分是網(wǎng)絡庫的核心部分,一個請求從開始到完成主要是由AFURLSessionManager、AFURLRequestSerialization、AFURLResponseSerialization這三部分協(xié)作完成的;

  • AFURLSessionManager負責網(wǎng)絡任務的創(chuàng)建和管理,并通過設置AFURLSessionManager為self.session的代理對象,負責實現(xiàn)網(wǎng)絡任務的代理回調方法;
  • self.requestSerializer會通過各種請求信息(包括請求方法、頭部信息、請求參數(shù)等)生成一個請求使用的NSMutableURLRequest實例;
  • self.responseSerializer會在請求完成時,對返回的數(shù)據(jù)進行驗證和解析,并交付解析后的格式化數(shù)據(jù);

c、AFNetworkReachabilityManager

網(wǎng)絡連接狀態(tài)發(fā)生變化的監(jiān)聽類,同時提供block形式的接口設置網(wǎng)絡狀態(tài)發(fā)生變化時的回調;并會在網(wǎng)絡連接狀態(tài)發(fā)生改變時發(fā)出對應的通知,因此如果我們APP的網(wǎng)絡層有需要監(jiān)聽網(wǎng)絡變化的需求時,可以通過監(jiān)聽這個類發(fā)出的通知來實現(xiàn);

關鍵函數(shù)包括以下兩個:

//開始監(jiān)聽網(wǎng)絡連接狀態(tài)
- (void)startMonitoring {
    、、、、
    /**核心代碼,調用SystemConfiguration.framework框架中的方法,
      增加主線程Runloop的commonModes下self.networkReachability對象的網(wǎng)絡狀態(tài)變化監(jiān)聽 */
    SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
    、、、、
}

//結束監(jiān)聽的方法
- (void)stopMonitoring {
    、、、、
    /**核心代碼,調用SystemConfiguration.framework框架中的方法, 取消在主線程Runloop的commonModes下面self.networkReachability的網(wǎng)絡狀態(tài)監(jiān)聽*/
     SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

d、AFSecurityPolicy

HTTPS的支持類,主要作用是在iOS系統(tǒng)驗證服務端返回的證書之前,在類中做一步提前驗證;如果在AFSecurityPolicy中的驗證不通過,則直接取消請求,驗證通過之后在走系統(tǒng)的認證;如果在AFSecurityPolicy中沒有設置具體的安全驗證策略,系統(tǒng)會加載默認的安全策略;關于這部分的更多介紹可以看這里或者更深入的AFNetworking之于https認證;

AFSecurityPolicy中提供了三種方式校驗服務器端給予的證書:

//三種校驗服務器端證書的方式
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,  //代表客戶端無條件地信任服務器端返回的證書(包括公信機構和自簽的證書),默認安全策略使用的是這種模式
    AFSSLPinningModePublicKey,  //代表客戶端將會使用本地保存的證書對服務器端返回的證書鏈中證書的PublicKey(公鑰)部分進行驗證,如果有一對公鑰匹配成功,則驗證通過;
    AFSSLPinningModeCertificate,  //代表客戶端將會使用本地保存的證書去匹配服務器端返回的證書鏈,如果有一對匹配成功則驗證通過;
};

e、UIKit擴展

這部分功能相對獨立,先從目錄結構看一下里面的內容:


UIKit擴展.png

在UIKit擴展中封裝了圖片下載和緩存功能,增加了一些系統(tǒng)控件的Category方法,為這些控件添加了一些便捷功能,如UIButton和UIImageView的圖片下載等;要對這部分做更詳細的了解可以看這里

2、AFNetworking核心部分的原理

a、AFURLSessionManager部分

我們從AFHTTPSessionManager對象的初始化開始,看看這部分的內部實現(xiàn):

//AFHTTPSessionManager的初始化最終都會統(tǒng)一到這個方法來完成
- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }
    //baseURL是一個url的前綴部分,如果有這部分信息,后面調用請求方法時只要給出接口的相對路徑就行;這里的作用是在baseURL后加上"/"
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    self.baseURL = url;  //賦值給baseURL屬性
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;
}

這里的初始化工作主要是通過調用父類的 [super initWithSessionConfiguration:configuration]; 初始化方法完成的,我們繼續(xù)進入到父類的初始化方法中查看初始化過程:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];  //設置默認的SessionConfiguration對象
    }
    self.sessionConfiguration = configuration;  //賦值給self.sessionConfiguration屬性

    //初始化self.session代理回調方法執(zhí)行的事件隊列,也就是所有的請求的代理回調方法都會在這個隊列中執(zhí)行
    self.operationQueue = [[NSOperationQueue alloc] init
    //queue的并發(fā)操作數(shù)設置為1
    self.operationQueue.maxConcurrentOperationCount = 1;

    //初始化網(wǎng)絡任務的NSURLSession對象,并設置回調代理,和代理回調方法的執(zhí)行隊列
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    self.responseSerializer = [AFJSONResponseSerializer serializer];  //初始化返回數(shù)據(jù)處理器,負責校驗和轉碼出對應的數(shù)據(jù)格式
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];  //設置默認安全策略

#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];  //網(wǎng)絡狀態(tài)監(jiān)測器,Apple Watch不需要這一對象
#endif

    /** 在AFN中每個網(wǎng)絡請求task都會關聯(lián)到框架中自定義的AFURLSessionManagerTaskDelegate類的對象中;通過這個對象再去處理task的delegate回調,這個對象相當于具體task的代理;
    這個字典就是用來存儲AFURLSessionManagerTaskDelegate網(wǎng)絡任務的對象的,并把task.taskIdentifier唯一標識作為key,任務對象作為value存入字典中;對這個字典的讀寫操作都需要加鎖;
    這個字典中存放了session中管理的所有task */
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    self.lock = [[NSLock alloc] init];  //操作AFURLSessionManagerTaskDelegate 詞典的鎖,確保詞典在多線程訪問時的線程安全
    self.lock.name = AFURLSessionManagerLockName;

    /** 以下這個方法的作用是用來異步的獲取當前session的所有未完成的task;其實在初始化中調用這個方法應該一個task都沒有;
      這個地方之所以要調用,是為了避免應用在后臺被掛起之后再回到前臺,重新恢復這個session,一些之前的后臺請求任務導致程序的異常crash。
      關于這個問題的討論:https://github.com/AFNetworking/AFNetworking/issues/3499 */
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }
        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }
        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}

到這里初始化基本完成了,初始化過程在代碼里已經(jīng)增加了注釋說明;初始化之后我們看一下具體的網(wǎng)絡請求的調用過程(這里使用GET請求舉例,不同的請求方法調用邏輯都一樣):

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure]; 
    [dataTask resume];  //生成網(wǎng)絡任務后,開始這個任務

    return dataTask;
}

//通過調用AFHTTPSessionManager中的這個方法生成task并返回
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    //根據(jù)傳遞進來的請求參數(shù)獲取,通過self.requestSerializer生成具體的request實例,這部分的內部調用我們放在下面單獨講解
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {  //生成請求request出錯時走的分支,拋出錯誤不繼續(xù)處理這個請求
        if (failure) {
            //這里的self.completionQueue是設置了用來執(zhí)行task完成后的回調隊列,如果沒有設置,則會在主線程隊列中完成回調
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }
        return nil;
    }

    __block NSURLSessionDataTask *dataTask = nil;
    //這里調用父類的方法,生成具體的task,并把請求結束后需要回調的block傳遞給completionHandler參數(shù)
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        //這個block會被關聯(lián)給具體的task任務,當task結束后,會通過代理方法執(zhí)行這個completionHandler的block,之后通過執(zhí)行failure或success的block回調到我們自己的業(yè)務層代碼
        if (error) {  //任務請求失敗的回調
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {  //任務請求成功的回調
                success(dataTask, responseObject);
            }
        }
    }];
    return dataTask;
}

以上代碼生成dataTask的過程是通過調用父類中的[self dataTaskWithRequest: uploadProgress: downloadProgress: completionHandler: ]方法實現(xiàn)的;同時生成的dataTask在返回前會在父類AFURLSessionManager中被存儲和管理,看一下父類中生成dataTask的調用:

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                             downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {

    __block NSURLSessionDataTask *dataTask = nil;
    /** 這里調用了一個作者寫的C函數(shù)來安全的創(chuàng)建一個網(wǎng)絡任務,主要是為了修復在iOS8.0之前的bug;
        在iOS8.0之前系統(tǒng)通過并行隊列創(chuàng)建任務時,可能會導致task的taskIdentifiers這個屬性值不唯一,這會導致taskIdentifiers作為key關聯(lián)delegate時出現(xiàn)錯亂,最終導致錯誤的completionHandler被調用;
        因此在這個函數(shù)的內部實現(xiàn)做了一個判斷,在iOS8.0之前使用自己創(chuàng)建的serial隊列同步的執(zhí)行創(chuàng)建任務block;*/
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    /** 這一步調用非常關鍵,其內部的邏輯涉及到AFURLSessionManager的核心部分;
        主要作用是生成delegate對象,管理這個dataTask和dataTask完成后的回調completionHandler */
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    return dataTask;  //返回創(chuàng)建好的任務給調用方
}

到這里可以看到task被生成出來并返回了,在返回task之前增加了一步很重要的調用,我們進入到 [self addDelegateForDataTask: uploadProgress: downloadProgress: completionHandler: ];函數(shù)內部看一下task被返回前是如何被處理的:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    //初始化任務的代理對象
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;  //建立代理對象與AFURLSessionManager對象的關聯(lián)(這個屬性是weak修飾的,所以不會引起循環(huán)引用)
    delegate.completionHandler = completionHandler;  //把回調句柄傳遞給代理對象對應的屬性

    //這個taskDescriptionForSessionTasks設置任務的taskDescription,在開始和掛起任務時,就是通過判斷任務的這個值來確認它屬于當前的session對象之后才Post通知的;
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    //這一步調用的作用是把代理對象和dataTask關聯(lián)到mutableTaskDelegatesKeyedByTaskIdentifier字典中進行存儲
    [self setDelegate:delegate forTask:dataTask];
    //設置上傳和下載的回調block
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

通過這個方法調用生成了AFURLSessionManagerTaskDelegate代理對象的實例,這個就是網(wǎng)絡任務的自定義代理。我們請求傳來的參數(shù)(completionHandler、uploadProgressBlock、downloadProgressBlock),都賦值給這個對象的屬性存儲起來了。我們再看看[self setDelegate:delegate forTask:dataTask];的內部調用:

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    //這里是一個系統(tǒng)的宏,當參數(shù)為空時,會觸發(fā)上一個斷言崩潰
    NSParameterAssert(task);
    NSParameterAssert(delegate);
    [self.lock lock];
    /** 在加鎖的情況下,把delegate代理對象存儲到mutableTaskDelegatesKeyedByTaskIdentifier字典中;
        這樣task代理對象就被存儲下來了(在AFURLSessionManager對象的mutableTaskDelegatesKeyedByTaskIdentifier屬性中);
        之后可以通過task.taskIdentifier從字典中獲取出對應的task代理對象,方便在AFURLSessionManager中傳遞部分代理回調方法給代理對象進行處理
         */
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    //增加網(wǎng)絡任務開始和掛起的通知監(jiān)聽;監(jiān)聽后在網(wǎng)絡任務開始和掛起時都會發(fā)出對應的通知
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

到這里task任務和delegate代理對象的關聯(lián)已經(jīng)完成了,并且存儲到了AFURLSessionManager對象的self.mutableTaskDelegatesKeyedByTaskIdentifier屬性中,之后可以通過這個字典屬性取出delegate對象使用,我們再看一下AFURLSessionManagerTaskDelegate代理對象內部是如何處理一個task的:

//首先是根據(jù)一個task完成初始化
- (instancetype)initWithTask:(NSURLSessionTask *)task {
    self = [super init];
    if (!self) {
        return nil;
    }
    _mutableData = [NSMutableData data];  //_mutableData屬性用來接收服務端返回的response數(shù)據(jù)
    _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];  //初始化上傳進度管理對象
    _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];  //初始化下載進度管理對象
    
    __weak __typeof__(task) weakTask = task;
    for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ]) {  //遍歷上傳和下載進度對象,對屬性進行賦值
        progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
        progress.cancellable = YES;
        //這里設置 progress的一些屬性,并且把兩者和task的任務狀態(tài)綁定在了一起;當設置progress的狀態(tài)時,都會觸發(fā)對應的block進行回調;
        progress.cancellationHandler = ^{
            [weakTask cancel];
        };
        progress.pausable = YES;
        progress.pausingHandler = ^{
            [weakTask suspend];
        };
#if __has_warning("-Wunguarded-availability-new")
        if (@available(iOS 9, macOS 10.11, *)) {
#else
        if ([progress respondsToSelector:@selector(setResumingHandler:)]) {
#endif
            progress.resumingHandler = ^{
                [weakTask resume];
            };
        }
        //添加網(wǎng)絡進度發(fā)生變化時的KVO監(jiān)聽,方便在進度發(fā)生變化時能實時的回調
        [progress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL];
    }
    return self;
}

//網(wǎng)絡進度發(fā)生變化后的KVO回調
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
   if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);  //執(zhí)行下載進度的回調block,并把當前的進度對象傳遞出去
        }
    } else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);  //執(zhí)行上傳進度的回調block,并把當前的進度對象傳遞出去
        }
    }
}

在AFURLSessionManagerTaskDelegate中還實現(xiàn)了一些必要的網(wǎng)絡請求代理回調方法,在看代理回調方法的內部實現(xiàn)前,我們先介紹一下這里的回調方法是如何調用過來的;

在配置好并返回一個task之后,會調用task的resume方法開始這個網(wǎng)絡任務,之后網(wǎng)絡任務的一系列代理回調方法將會被調用;

再看一下之前的初始化方法self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];;我們是把AFURLSessionManager作為代理回調對象的;也就是網(wǎng)絡任務的所有代理回調方法都是在AFURLSessionManager中去回調和實現(xiàn)的,這個對象實現(xiàn)的代理包括以下這些:

//AFURLSessionManager實現(xiàn)了以下代理方法:

 ## `NSURLSessionDelegate`
 - `URLSession:didBecomeInvalidWithError:`
 - `URLSession:didReceiveChallenge:completionHandler:`
 - `URLSessionDidFinishEventsForBackgroundURLSession:`
 ### `NSURLSessionTaskDelegate`
 - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`
 - `URLSession:task:didReceiveChallenge:completionHandler:`
 - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`
 - `URLSession:task:needNewBodyStream:`
 - `URLSession:task:didCompleteWithError:`
 ### `NSURLSessionDataDelegate`
 - `URLSession:dataTask:didReceiveResponse:completionHandler:`
 - `URLSession:dataTask:didBecomeDownloadTask:`
 - `URLSession:dataTask:didReceiveData:`
 - `URLSession:dataTask:willCacheResponse:completionHandler:`
 ### `NSURLSessionDownloadDelegate`
 - `URLSession:downloadTask:didFinishDownloadingToURL:`
 - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`
 - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`

每個代理方法都有不同的作用,會在不同的事件發(fā)生時觸發(fā)回調;

這里需要補充解釋的一點是,我們只在初始化方法上設置了一個delegate,系統(tǒng)卻可以觸發(fā)的四個不同代理中的回調方法;原因是這些代理之間是存在繼承關系的,而在NSURLSession實現(xiàn)中,只要設置了NSURLSessionDelegate這個代理,它會去判斷這些所有的代理,對象是否respondsToSelector這些代理中的方法,如果響應了就會去回調。

在回調發(fā)生時,以下幾個方法的調用被轉發(fā)到了AFURLSessionManagerTaskDelegate對象(網(wǎng)絡任務代理對象)中:

//AFURLSessionManagerTaskDelegate對象實現(xiàn)了以下代理方法,并通過以下方法的內部實現(xiàn)把task對應的數(shù)據(jù)回調出去:

 ### `NSURLSessionTaskDelegate`
 - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`
 - `URLSession:task:didCompleteWithError:`
 ### `NSURLSessionDataDelegate`
 - `URLSession:dataTask:didReceiveData:`
 ### `NSURLSessionDownloadDelegate`
 - `URLSession:downloadTask:didFinishDownloadingToURL:`
 - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`
 - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`

接下來我們看一部分delegate方法內部的實現(xiàn),和這些delegate方法被觸發(fā)調用的時機;以及那些需要轉發(fā)的代理方法被轉發(fā)到AFURLSessionManagerTaskDelegate代理對象中的過程,在代理對象中接收到轉發(fā)的消息后又是怎么去響應處理的:

NSURLSessionDelegate代理方法

//當前這個session已經(jīng)失效時,該代理方法被調用
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
    /**self.sessionDidBecomeInvalid是一個私有屬性,他的類型是一個typedef定義的block,在頭文件里開放了這個block的set方法;通過調用set方法可以給這個屬性賦值。
      AFURLSessionManager中有一些列的類似屬性,都可以通過調用頭文件的set方法進行設置賦值;這些block屬性分別對應不同的代理回調方法,設置block方便用戶實現(xiàn)自己的回調邏輯;
      如果設置了對應的block,就會在代理回調方法中觸發(fā)這個block的回調執(zhí)行*/
    if (self.sessionDidBecomeInvalid) {  
        self.sessionDidBecomeInvalid(session, error);
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];  //發(fā)出了一個對應事件的通知,方便用戶有需要時進行監(jiān)聽
}

- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    //主要作用是在方法中完成HTTPS認證挑戰(zhàn)
}

NSURLSessionTaskDelegate代理方法

 //被服務器重定向的時調用
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest *))completionHandler 
{
NSURLRequest *redirectRequest = request;
    if (self.taskWillPerformHTTPRedirection) {
        //如果有用戶自定義的回調block,則執(zhí)行這個回調block,生成重定向的request
        redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }
    if (completionHandler) {
        completionHandler(redirectRequest);  //通過重定向的request重新請求
    }
}

//當每次發(fā)送數(shù)據(jù)給服務器時,會觸發(fā)這個方法回調
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    int64_t totalUnitCount = totalBytesExpectedToSend;  //預期要發(fā)送的總字節(jié)數(shù)
    if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
        //如果totalUnitCount獲取失敗,就使用HTTP header中的Content-Length作為totalUnitCount
        NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
        if(contentLength) {
            totalUnitCount = (int64_t) [contentLength longLongValue];
        }
    }
    //通過task從mutableTaskDelegatesKeyedByTaskIdentifier屬性中獲取出AFN 的task代理對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];  
    if (delegate) {  //傳遞事件到代理對象中
        [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
    }
    if (self.taskDidSendBodyData) {  //如果通過set方法設置了回調block,此時就執(zhí)行這個block
        self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
    }
}

//這個方法在一個任務請求完成時調用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
     //通過task從mutableTaskDelegatesKeyedByTaskIdentifier屬性中獲取出AFN 的task代理對象
    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) {  //set方法設置了block就回調
        self.taskDidComplete(session, task, error);
    }
}

以上回調方法的作用和調用時機在注釋中已經(jīng)說明,還里需要補充說明的是:

  • 以上有兩個方法都出現(xiàn)了把回調事件轉發(fā)到delegate代理對象的調用過程,通過[self delegateForTask:task]方法獲取出代理對象,在調用delegate對象中的對應方法,實現(xiàn)方法的轉發(fā);

我們知道之前task和delegate對象通過把task.taskIdentifier作為key存儲到了self.mutableTaskDelegatesKeyedByTaskIdentifier字典中,因此此處可以通過task.taskIdentifier在字典中取出對應的delegate對象;并通過獲取到的delegate對象進行事件的轉發(fā);

我們在看一下當任務完成時消息轉發(fā)到delegate對象后的處理邏輯:

//代理對象中處理一個任務結束時的回調,通過這個方法把數(shù)據(jù)和事件回調到用戶手里
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    __strong AFURLSessionManager *manager = self.manager;
    __block id responseObject = nil;

    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];  //存儲這個請求的關聯(lián)數(shù)據(jù),用于發(fā)送請求結束的通知
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];  //這里把數(shù)據(jù)拷貝的局部變量,然后銷毀掉接收存儲數(shù)據(jù)的屬性,減少內存開銷,優(yōu)化性能;
        self.mutableData = nil;
    }
    if (self.downloadFileURL) {  //如果返回的是資源地址,則把地址存入字典中
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {  //如果返回的是數(shù)據(jù),則把數(shù)據(jù)放入字典內
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    if (error) {  //當回調中包含error信息時,執(zhí)行error的回調
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {  //回調到用戶的block中
                self.completionHandler(task.response, responseObject, error);
            }
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {  //當回調正常,沒有出現(xiàn)錯誤時,首先在處理返回數(shù)據(jù)的隊列中執(zhí)行返回數(shù)據(jù)的處理
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];  //通過responseSerializer對象驗證和序列化返回的數(shù)據(jù)
            if (self.downloadFileURL) {  //如果時地址數(shù)據(jù),則把返回數(shù)據(jù)賦值成地址
                responseObject = self.downloadFileURL;  
            }
            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }
            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }
            //取出請求完成的回調隊列,并執(zhí)行回調的completionHandler把事件回調給用戶
            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);
                }
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}

到此我們看到了一個task任務在請求結束時,是如何通過代理對象處理返回的結果,并把結果回調到用戶手中的;

NSURLSessionDataDelegate代理回調方法

//當一個任務轉變成下載任務時調用
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];   //獲取出AFN的task代理對象
    if (delegate) {
        [self removeDelegateForTask:dataTask];  //把原來的任務移除
        [self setDelegate:delegate forTask:downloadTask];  //把新的下載任務添加進去
    }
    if (self.dataTaskDidBecomeDownloadTask) {  //設置了回調block,則執(zhí)行這個block
        self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
    }
}

//當接收到服務端返回的階段性數(shù)據(jù)時調用
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];   //獲取出AFN的task代理對象
    [delegate URLSession:session dataTask:dataTask didReceiveData:data];   //把事件傳遞給代理對象處理
    if (self.dataTaskDidReceiveData) {  //設置了block回調,則執(zhí)行
        self.dataTaskDidReceiveData(session, dataTask, data);
    }
}

NSURLSessionDownloadDelegate代理回調方法

//當一個下載任務完成時觸發(fā)的回調方法
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];   //獲取出AFN的task代理對象
    if (self.downloadTaskDidFinishDownloading) {  //如果通過set方法設置了對應的回調block,則執(zhí)行,并在執(zhí)行成功后直接返回
        NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (fileURL) {
            delegate.downloadFileURL = fileURL;
            NSError *error = nil;
            if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
            }
            return;
        }
    }
    if (delegate) {  //如果沒有通過上面的block執(zhí)行完回調,則把事件傳遞給代理對象處理
        [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
    }
}

//下載過程中每獲取到一部分新的數(shù)據(jù),都會觸發(fā)這個方法的回調
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];   //獲取出AFN的task代理對象
    if (delegate) {  //把事件傳遞給代理對象處理
        [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
    }
    if (self.downloadTaskDidWriteData) {  //如果設置了這個事件的回調block,則執(zhí)行對應的回調block
        self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    }
}

//下載任務中斷后重新開始時調用,會從之前的中斷數(shù)據(jù)節(jié)點處繼續(xù)開始下載;這個方法實現(xiàn)的是斷點續(xù)傳功能
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];  //獲取出AFN的task代理對象
    if (delegate) {  //把事件傳遞給代理對象處理
        [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
    }
    if (self.downloadTaskDidResume) {   //如果設置了這個事件的回調block,則執(zhí)行對應的回調block
        self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
    }
}

考慮到還有AFURLRequestSerializationAFURLResponseSerialization部分沒有講解,限于文章的篇幅,關于delegate回調方法的講解就到這里;我們并沒有對所有的代理回調方法都進行說明,如果要了解更多的回調方法的作用,可以在源碼中“option+單擊”查看對應方法的官方說明;

接下來會分析構造NSURLMuatbleRequest實例的過程和對服務端返回數(shù)據(jù)的驗證與序列化的過程;

b、AFURLRequestSerialization部分

AFURLSessionManager中通過self.session調用NSURLSession對象的系統(tǒng)方法(如downloadTaskWithRequest:)生成網(wǎng)絡任務task時,需要有一個NSURLRequest的對象作為系統(tǒng)方法的參數(shù),方法內部會使用這個參數(shù)生成對應的網(wǎng)絡任務;

AFURLRequestSerialization的作用就是用來生成并返回NSURLRequest類型的參數(shù)的,在AFURLRequestSerialization內部會根據(jù)不同的請求信息生成出對應的NSURLRequest實例;我們看一下生成request實例的起始調用(以下方法只保留了創(chuàng)建request的代碼):

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    //通過這一步調用生成request參數(shù)
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {  
        //生成參數(shù)出錯的處理
    }
    、、、
}

可以看到創(chuàng)建一個網(wǎng)絡task的第一步,是通過調用self.requestSerializer的方法生成request實例;我們進入到生成request方法的內部查看生成request的過程:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    //使用系統(tǒng)的宏斷言,當參數(shù)為空時拋出異常
    NSParameterAssert(method);
    NSParameterAssert(URLString);
    NSURL *url = [NSURL URLWithString:URLString];  //通過URLString生成請求的url
    NSParameterAssert(url);
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;  //設置HTTP請求方法
    //遍歷request請求的所有屬性
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        //把設置了新值取代默認值的屬性判斷出來,并把屬性的新值設置給mutableRequest對象
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }
    //對請求的參數(shù)parameters進行序列化處理,并返回處理好參數(shù)后的request對象
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    return mutableRequest;
}

可以看到在方法內部首先通過URLString參數(shù)創(chuàng)建了mutableRequest對象,然后在設置mutableRequest對象的一些參數(shù);配合方法內的注釋,以下在對兩個地方做補充說明:

  • 1、代碼中for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths())循環(huán)的作用;

我們先看一下循環(huán)條件中AFHTTPRequestSerializerObservedKeyPaths函數(shù)的內部實現(xiàn):

static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
    });
    return _AFHTTPRequestSerializerObservedKeyPaths;
}

這個方法的作用是獲取出AFHTTPRequestSerializer在頭文件申明的所有屬性(包括allowsCellularAccess、cachePolicy、HTTPShouldHandleCookies、HTTPShouldUsePipelining、networkServiceType、timeoutInterval),通過數(shù)組的形式返回;然后循環(huán)遍歷這些屬性;

這些屬性在NSMutableURLRequest中都有對應的同名屬性,并且存在默認值,在AFHTTPRequestSerializer中增加這些屬性的申明,是方便用戶通過AFHTTPRequestSerializer對象去設置對應的屬性值覆蓋原有的默認值;

在循環(huán)內部通過[self.mutableObservedChangedKeyPaths containsObject:keyPath]的進行判斷,self.mutableObservedChangedKeyPaths屬性中存儲了所有設置了新值的屬性,因此可以判斷出keyPath是否是設置了新值的;如果設置了(即條件成立),就把新的值設置給NSMutableURLRequest的對應屬性覆蓋掉原有的默認值;

AFHTTPRequestSerializer中是通過KVO的方式觀察這些屬性值的變化的:

//在初始化方法中,增加了這些屬性的KVO監(jiān)聽
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
   if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
        [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];  //出現(xiàn)新值時觸發(fā)回調方法
    }
 }

//KVO回調方法
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(__unused id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (context == AFHTTPRequestSerializerObserverContext) {
        if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {          //當沒有新的值時,把self.mutableObservedChangedKeyPaths中對應的屬性移除掉
            [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else {  //當屬性設置了新的值時,把對應的屬性添加進self.mutableObservedChangedKeyPaths中
            [self.mutableObservedChangedKeyPaths addObject:keyPath];
        }
    }
}
  • 2、生成request方法中第二個需要說明的是,在給request對象設置請求信息的過程中調用了[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error];方法,這是一個<AFURLRequestSerialization>協(xié)議方法,不同請求數(shù)據(jù)的設置都需要去實現(xiàn)這個協(xié)議方法;

我們看一下在AFHTTPRequestSerializer這個類中關于這個協(xié)議方法的實現(xiàn):

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);  //參數(shù)判空
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    //枚舉存儲在self.HTTPRequestHeaders對象中的所有請求頭部信息,并設置到mutableRequest請求實例的頭部信息中
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];
    NSString *query = nil;  //接收序列化后的參數(shù)
    if (parameters) {  //請求參數(shù)不為空,則做參數(shù)的序列化處理
        if (self.queryStringSerialization) {  //如果設置了序列化參數(shù)的回調block,則通過這個block完成序列化過程
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);
            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }
                return nil;
            }
        } else {  //不存在用戶自定義的序列化block,則通過框架提供的方式序列化參數(shù)
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);  //調用函數(shù)完成參數(shù)的序列化
                    break;
            }
        }
    }
    //self.HTTPMethodsEncodingParametersInURI中存儲了(GET、HEAD、DELETE)三種請求方式,這里判斷如果是這三種請求方式則把序列化的參數(shù)添加到請求URL上發(fā)送;如果不是這三種方式(POST、PUT等請求),則把序列化好的參數(shù)設置到http的body種進行發(fā)送;
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query && query.length > 0) {
            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
        }
    } else {
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";  //為空時,先設置成空字符串,避免編碼時異常
        }
        //如果不存在指定的body編碼方式,則使用application/x-www-form-urlencoded默認編碼方式;這部分需要對http協(xié)議本身有一定的了解,body數(shù)據(jù)支持多種編碼方式(包括json、xml、multipart/form-data等)
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];  //設置Content-Type內容編碼方式
        }
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];  //把數(shù)據(jù)編碼成NSData類型,傳遞到請求body中
    }
    return mutableRequest;
}

以上代碼的作用是把用戶設置的請求header信息和序列化后的parameters參數(shù)設置到mutableRequest實例中;

其中self.HTTPRequestHeaders獲取到的是用戶通過調用- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;方法設置的所有請求頭部信息,是一個NSDictionary類型的參數(shù);通過枚舉這個參數(shù)內部的值給request設置請求頭部信息;

頭部信息設置好之后,在對parameters參數(shù)做序列化處理;可以通過自定義的block做參數(shù)的序列化,具體的序列化邏輯由用戶在自己的回調block中定義;還可以通過系統(tǒng)的默認方式進行parameters的序列化,我們看一下默認的序列化過程:

//序列化參數(shù)的方法
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    //通過調用AFQueryStringPairsFromDictionary方法生成參數(shù)的AFQueryStringPair對應數(shù)組
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        //通過pair對象的URLEncodedStringValue方法對參數(shù)做序列化
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }
    return [mutablePairs componentsJoinedByString:@"&"];  //通過&拼接數(shù)組內的元素
}

NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    //往下調用
    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
 
//負責完成參數(shù)的組裝,參數(shù)內部的key-value數(shù)據(jù)都會轉化成AFQueryStringPair對象
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];  //接收結果的數(shù)組
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];  //排序用的對象
    //如果是dic、array、set類型就遞歸調用自己;不是這幾種類型就通過AFQueryStringPair初始化數(shù)據(jù)并存儲
    if ([value isKindOfClass:[NSDictionary class]]) {  //對字典類型的處理
        NSDictionary *dictionary = value;
        // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            id nestedValue = dictionary[nestedKey];
            if (nestedValue) {
                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
            }
        }
    } else if ([value isKindOfClass:[NSArray class]]) {
        NSArray *array = value;
        for (id nestedValue in array) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
        }
    } else if ([value isKindOfClass:[NSSet class]]) {
        NSSet *set = value;
        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
        }
    } else {
        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
    }
    return mutableQueryStringComponents;
}

以上就是完成請求參數(shù)序列化的過程;內部使用到了AFQueryStringPair對象,這個對象的定義非常簡單,只有兩個屬性和兩個方法:

@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;

- (instancetype)initWithField:(id)field value:(id)value {
    self = [super init];
    if (!self) {
        return nil;
    }
    self.field = field;
    self.value = value;
    return self;
}

//對參數(shù)編碼后返回
- (NSString *)URLEncodedStringValue {
    if (!self.value || [self.value isEqual:[NSNull null]]) {  //value值為空時的處理
        //對field的值進行編碼,http協(xié)議是通過ASCII 碼傳輸?shù)?,需要對參?shù)做編碼處理
        return AFPercentEscapedStringFromString([self.field description]);  
    } else {
        //field和value都存在時,對數(shù)據(jù)做的編碼處理
        return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
    }
}

完成參數(shù)的序列化之后,會把parameters序列化后的結果設置到request中;

到此生成一個NSMutableURLRequest實例的內部調用過程就完成了,在AFURLRequestSerialization中還有幾種不同的生成request實例的方式;其中Content-Typemultipart/form-data方式的請求,生成request的過程會更復雜一些,但是基本流程是一樣的;

c、AFURLResponseSerialization部分

這部分主要用來完成對服務端返回數(shù)據(jù)的驗證和序列化,把返回的NSData數(shù)據(jù)解析成對應格式的數(shù)據(jù),包括JSON、XML和圖片類型的數(shù)據(jù);

數(shù)據(jù)的解析是從網(wǎng)絡代理對象AFURLSessionManagerTaskDelegate實例的以下代理方法開始的(這個方法在請求完成時回調,方法內部分代碼已經(jīng)省略了):

//在請求完成時回調到這個方法中,在方法內部會處理請求返回的數(shù)據(jù)(這里為了方便查看,方法中只保留了解析返回數(shù)據(jù)的邏輯,和解析數(shù)據(jù)無關的代碼已經(jīng)過濾)
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    /**由于對返回數(shù)據(jù)的解析是一個比較耗時的操作,所以這里使用一個單獨的線程隊列處理數(shù)據(jù)的解析*/
    dispatch_async(url_session_manager_processing_queue(), ^{
        NSError *serializationError = nil;
        //調用數(shù)據(jù)解析方法對數(shù)據(jù)進行解析,并得到解析的結果對象
        responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
        //在設置好的group和queue中執(zhí)行完成的回調self.completionHandler,沒有設置就在默認的組和主線程中執(zhí)行回調
        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);
           }
        });
    });
}

以上代碼通過調用 [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]方法可以完成返回數(shù)據(jù)的解析;

我們從[manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]方法的申明開始,了解AFURLResponseSerialization內部的設計和數(shù)據(jù)解析的過程;

首先,responseObjectForResponse:data:error:是一個協(xié)議方法,它的申明如下:

@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>

- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                           data:(nullable NSData *)data
                          error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

@end

數(shù)據(jù)解析基類AFHTTPResponseSerializer會遵循這個協(xié)議,并實現(xiàn)協(xié)議方法,在方法中對返回的數(shù)據(jù)做一些簡單的驗證(主要驗證返回結果的狀態(tài)碼和Content-Type的MIME數(shù)據(jù)格式);把真正的數(shù)據(jù)解析工作留給對應的子類完成;

特定類型的數(shù)據(jù)解析器(AFJSONResponseSerializer、AFXMLParserResponseSerializer等)都是繼承自AFHTTPResponseSerializer的,并在解析器內部去重新實現(xiàn)協(xié)議方法,在協(xié)議方法內完成對應格式的數(shù)據(jù)解析,并把解析后的結果數(shù)據(jù)返回;

manager.responseSerializer包括以下幾種不同的數(shù)據(jù)解析器類型:

/**通過NSJSONSerialization對象把返回的數(shù)據(jù)解析成JSON格式*/
self.responseSerializer = [AFJSONResponseSerializer serializer];
/**通過NSXMLParser對象把返回的數(shù)據(jù)解析成XML格式*/
self.responseSerializer = [AFXMLParserResponseSerializer serializer];
/**通過NSXMLDocument對象把返回的數(shù)據(jù)解析成XML格式*/
self.responseSerializer = [AFXMLDocumentResponseSerializer serializer];
/**通過NSPropertyListSerialization對象把返回的數(shù)據(jù)解析成XML格式,*/
self.responseSerializer = [AFPropertyListResponseSerializer serializer];
/**把返回的數(shù)據(jù)解析成圖片數(shù)據(jù)并返回UIImage*/
self.responseSerializer = [AFImageResponseSerializer serializer];
/**返回的數(shù)據(jù)格式不單一時,通過這種方式初始化*/
self.responseSerializer = [AFCompoundResponseSerializer serializer];

到這里我們大概知道了AFURLResponseSerialization關于數(shù)據(jù)解析的內部設計了:

  • 在這個文件中,首先會申明一個協(xié)議,協(xié)議中申明了具體的用于解析數(shù)據(jù)的方法;
  • 不同的數(shù)據(jù)解析器需要遵循這個協(xié)議,并實現(xiàn)特定的數(shù)據(jù)解析邏輯;
  • self.responseSerializer對象用AFHTTPResponseSerializer <AFURLResponseSerialization> *進行申明,然后用AFHTTPResponseSerializer的解析器子類去實例化對象,方便在調用解析方法時,能調用到對應子類實現(xiàn)的解析方法;

我們在看一下特定格式數(shù)據(jù)的解析過程,這里以JSON格式的解析為例(不同類型的解析過程源代碼都比較相似):

#pragma mark - AFURLResponseSerialization
//在JSON解析類中實現(xiàn)的AFURLResponseSerialization協(xié)議方法
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    //調用父類中的方法,對返回的數(shù)據(jù)做初步驗證
    if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
        //如果驗證不通過,并且當error不存在、或者是返回解析結果為空
        if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
            return nil;
        }
    }
    //這里為了規(guī)避一個bug,簡單說就是對NSJSONSerialization來說一個單獨的空格" "數(shù)據(jù)輸入是不合理的,所以需要把這種把這種情況排除掉; 
    BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
    if (data.length == 0 || isSpace) {
        return nil;  //返回的結果為空時,解析后同樣為nil
    }
    NSError *serializationError = nil;
    /**調用系統(tǒng)方法解析json數(shù)據(jù);不同類型的解析器的實現(xiàn)內部,主要不同就在這里,xml等會有不同的系統(tǒng)解析方法*/
    id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];  
    if (!responseObject)  {  //系統(tǒng)方法沒有解析出數(shù)據(jù),返回nil對象
        if (error) {
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
        return nil;
    }
    if (self.removesKeysWithNullValues) {  //如果開啟了排空屬性,就把解析后的內容里面的空字段過濾掉
        return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);  //返回過濾后的結果
    }
    return responseObject;  //返回解析后的結果
}

以上解析返回數(shù)據(jù)的內部調用都添加了注釋進行說明,其中內部調用到的幾個函數(shù)需要在補充說明:

  • 在開始解析數(shù)據(jù)之前,先通過一個判斷對數(shù)據(jù)做了一次驗證;調用的方法是[self validateResponse:(NSHTTPURLResponse *)response data:data error:error],這個方法是實現(xiàn)在父類中的方法,每種特定數(shù)據(jù)類型的解析都會先通過這個方法對返回的數(shù)據(jù)做一個驗證,驗證的內容包括返回的狀態(tài)碼和支持的數(shù)據(jù)類型,方法的實現(xiàn)如下
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    BOOL responseIsValid = YES;  //存儲驗證是否通過的BOOL值
    NSError *validationError = nil;
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        /**驗證返回的數(shù)據(jù)類型是否正確;JSON的解析器在初始化時設置了self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];的類型支持;這里的判斷條件是服務端返回的數(shù)據(jù)類型存在,且數(shù)據(jù)不為空,但是類型卻不在初始化指定的幾種類型中*/
        if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
            !([response MIMEType] == nil && [data length] == 0)) {
            //此時數(shù)據(jù)類型驗證不通過,如果返回有數(shù)據(jù)則需要封裝返回結果的錯誤信息字典內容
            if ([data length] > 0 && [response URL]) {
                NSMutableDictionary *mutableUserInfo = [@{
                                                          NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
                                                          NSURLErrorFailingURLErrorKey:[response URL],
                                                          AFNetworkingOperationFailingURLResponseErrorKey: response,
                                                        } mutableCopy];
                if (data) {
                    mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
                }
                validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
            }
            responseIsValid = NO;  //類型驗證失敗
        }
        /**對返回的狀態(tài)碼進行驗證,狀態(tài)碼的初始化設置為self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; 如果返回的狀態(tài)碼不在初始化的這個集合中,則驗證失敗,可以理解為200開頭的狀態(tài)碼都是請求成功的情況,其他情況都屬于請求失敗了*/
        if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
            NSMutableDictionary *mutableUserInfo = [@{
                                               NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
                                               NSURLErrorFailingURLErrorKey:[response URL],
                                               AFNetworkingOperationFailingURLResponseErrorKey: response,
                                       } mutableCopy];

            if (data) {
                mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
            }
            validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
            responseIsValid = NO;
        }
    }
    if (error && !responseIsValid) {
        *error = validationError;  //把封裝好的驗證錯誤信息賦值給error
    }
    return responseIsValid;  //返回驗證是否通過的結果
}
  • 在解析數(shù)據(jù)方法內,還包括了以下幾個方法的調用:
    1、AFErrorWithUnderlyingError:方法的作用是把JSON解析的錯誤,封裝到我們需要返回給用戶的error上;
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
    if (!error) {
        return underlyingError;
    }
    if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
        return error;
    }
    NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
    mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
    return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}

2、AFErrorOrUnderlyingErrorHasCodeInDomain:方法的作用是判斷是不是我們自己在驗證返回數(shù)據(jù)時寫入的錯誤信息,是的話返回YES;

我們在驗證返回數(shù)據(jù)的方法里,在驗證失敗時寫入的code和domain兩個參數(shù)分別為NSURLErrorCannotDecodeContentData、AFURLResponseSerializationErrorDomain;調用這個方法時傳遞的code和domain同樣也是這兩個值;

static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
    //判斷傳遞過來的域名和code碼是否與error中的一致;
    if ([error.domain isEqualToString:domain] && error.code == code) {  
        return YES;
    } else if (error.userInfo[NSUnderlyingErrorKey]) {
        return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);  //遞歸調用自己判斷錯誤信息
    }
    return NO;
}

3、AFJSONObjectByRemovingKeysWithNullValues:函數(shù)的作用是在解析結果對象中移除那些值為nil或[NSNull null]的鍵值對;

static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
    if ([JSONObject isKindOfClass:[NSArray class]]) {
        //初始化用來接收移除空數(shù)據(jù)后的結果集
        NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
        for (id value in (NSArray *)JSONObject) {
            //遍歷數(shù)組中的元素,遞歸調用移除空值的方法
            [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];  
        }
        //根據(jù)解析類型時mutable還是非mutable的,返回對應的結果集
        return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
    } else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
        //對NSDictionary類型需要移除空值字段
        NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
        for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
            id value = (NSDictionary *)JSONObject[key];
            if (!value || [value isEqual:[NSNull null]]) {  //判斷是否為空,為空時移除
                [mutableDictionary removeObjectForKey:key];
            } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
                mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);  //如果不為空則遞歸調用遍歷子集
            }
        }
        //返回可變或不可變集合
        return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
    }
    return JSONObject;
}

這個方法在調用前有一層判斷,只有開啟了移除空鍵值對的屬性時,才會調用該方法做移除空值操作;

到此JSON數(shù)據(jù)類型的解析就講完了,其他數(shù)據(jù)類型的解析過程基本是類似的,就不一一列舉了;

最后

這篇文章主要對網(wǎng)絡請求任務的管理、請求的生成與返回的數(shù)據(jù)解析過程做了比較深入的講解;網(wǎng)絡庫涉及的內容本身也比較多,我們從中過濾了一些對 理解這個庫原理本身影響不大的部分過程代碼,包括一系列的網(wǎng)絡代理回調方法等(關于這部分的細節(jié)可以再查看對應主題的文章);希望能加深一點你對這個庫的理解。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容