AFNetWorking原始碼之AFURLSessionManager

huang303513發表於2017-04-19

1 概述

AFNetWorking基本上是所有iOS專案的標配。現在升級帶最新版的3.X了。得益於蘋果從NSURLConnection升級到NSURLSession,AFN也實現了api的簡化,同時功能卻一點沒少。我們來看一下AFN3.X的目錄結構:

  • AFNetWorking 這個檔案是一個標頭檔案。啥也沒做,就是引入了其他檔案方便使用。

  • AFURLSessionManager 這個檔案是核心類,基本上通過它來實現了大部分核心功能。負責請求的建立、管理、銷燬、安全、請求重定向、請求重啟等各種功能。他主要實現了NSURLSessionNSRULSessionTask的封裝。

  • AFHTTPSessionManager 這個檔案是AFURLSessionManager的子類。主要實現了對HTTP請求的優化。

  • AFURLRequestSerialization 這個主要用於請求頭的編碼解碼、序列化、優化處理、簡化請求拼接過程等。

  • AFURLResponseSerialization 這個主要用於網路返回資料的序列化、編碼解碼、序列化、資料處理等。

  • AFSecurityPolicy 這個主要用於請求的認證功能。比如https的認證模式等。

  • AFNetworkReachabilityManager 這個主要用於監聽網路請求狀態變化功能。

首先說明,看AFN原始碼之前一定要搞清楚NSURLSession系列的api,這樣能讓你事半功倍,具體可以看AFNetWorking原始碼之NSRULSession系列概述。在這篇文章裡,我們主要講解AFURLSessionManager的實現原理和封裝過程。首先我們通過一個簡單的網路請求看一下他的基本用法(大部分都是非必須的,這裡為了掩飾寫出來):

- (IBAction)clickButton:(id)sender {
    //通過預設配置初始化Session
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    //設定網路請求序列化物件
    AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer];
    [requestSerializer setValue:@"test" forHTTPHeaderField:@"requestHeader"];
    requestSerializer.timeoutInterval = 60;
    requestSerializer.stringEncoding = NSUTF8StringEncoding;
    //設定返回資料序列化物件
    AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
    manager.responseSerializer = responseSerializer;
    //網路請求安全策略
    if (true) {
        AFSecurityPolicy *securityPolicy;
        securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
        securityPolicy.allowInvalidCertificates = false;
        securityPolicy.validatesDomainName = YES;
        manager.securityPolicy = securityPolicy;
    } else {
        manager.securityPolicy.allowInvalidCertificates = true;
        manager.securityPolicy.validatesDomainName = false;
    }
    //是否允許請求重定向
    if (true) {
        [manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest *(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request) {
            if (response) {
                return nil;
            }
            return request;
        }];
    }
    //監聽網路狀態
    [manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        NSLog(@"%ld",(long)status);
    }];
    [manager.reachabilityManager startMonitoring];
    
    NSURL *URL = [NSURL URLWithString:bigPic];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
        NSLog(@"下載進度:%lld",downloadProgress.completedUnitCount);
    } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
        NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
        NSLog(@"fileURL:%@",[fileURL absoluteString]);
        return fileURL;
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:filePath]];
        NSLog(@"File downloaded to: %@", filePath);
    }];
    [downloadTask resume];
}

通過這個請求,我們發現AFURLSessionManager要負責以下幾塊功能。

  • 初始化和管理NSURLSession,通過它來建立和管理各種Task。

  • 初始化和管理NSRULSessionTask,通過不同task來傳送不同請求。

  • 管理各種認證功能、安全功能、請求重定向、資料處理。

  • 管理和組織每個task的各種狀態管理和通知管理。不同task的回撥處理。

  • 幫我們管理和處理了NSRULSession系列api的各種代理方法。簡化了我們的處理。

2 AFURLSessionManager的宣告分析

AFURLSessionManager根據一個指定的NSURLSessionConfiguration建立和管理一個NSURLSession物件。並且這個物件實現了<NSURLSessionTaskDelegate>, <NSURLSessionDataDelegate>, <NSURLSessionDownloadDelegate>, 和 <NSURLSessionDelegate>這幾個協議的協議方法。同時實現NSSecureCodingNSCopying來實現歸檔解檔和copy功能。

2.1 AFURLSessionManager的初始化api

這些api主要用於初始化、安全策略、網路狀態監聽等:

interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
//指定的初始化方法、通過他來初始化一個Manager物件。
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration 
//AFURLSessionManager通過session來管理和建立網路請求。一個manager就實現了對這個session的管理,他們是一一對應的關係。
@property (readonly, nonatomic, strong) NSURLSession *session;
//處理網路請求回撥的操作佇列,就是我們初始化session的時候傳入的那個OperationQueue引數。如果不傳入,預設是MainOperationQueue。
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
//對返回資料的處理都通過這個屬性來處理,比如資料的提取、轉換等。預設是一個`AFJSONResponseSerializer`物件用JSON的方式解析。
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
//用於指定session的安全策略。用於處理信任主機和證書認證等。預設是`defaultPolicy`。
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
//觀測網路狀態的變化,具體可以看我的Demo用法。
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
@end

2.2 AFURLSessionManager獲取Task的api

這部分api主要是任務的建立、任務的分類、任務完成佇列處理、特殊情況的任務重新建立等:

//當前session建立的所有Task,這個是下面三種task的總和。
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
//當前session建立的DataTask
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
//當前session建立的uploadTask
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
//當前session建立的downloadTask
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;

//用於處理任務回撥的GCD物件,預設是dispatch_main_queue。
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
//用於處理任務回撥的GCD的group物件,如果不初始化、則一個預設的Group被使用。
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
//在iOS7的環境下,我們通過background模式的session建立的uploadTask有時會是nil,如果這個屬性是yes,AFN會嘗試再次建立uploadTask。
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
//廢除manager對應的Session。通過傳入的引數來決定是否立即取消已經用session發出去的任務。
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;

2.3 AFURLSessionManager為管理Task建立Block

AFURLSessionManager提供了很多建立Task的api。並且提供了很多處理Task的Block。應該說著幾個api就是AFN為我們提供的最大價值,他把所有delegate方法細節都處理好。直接提供給我們一些最實用的api,我們就不用去管理session系列繁瑣的delegate方法了。

//建立一個NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler;
//建立一個NSURLSessionDataTask,並且能獲取上傳或者下載進度
- (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;

//建立一個上傳Task,並且指定上傳檔案的路徑。
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;
////建立一個上傳Task,並且指定上傳的資料。
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromData:(nullable NSData *)bodyData
                                         progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
//建立一個uploadTask,然後上傳資料
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                 progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                        completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
//新建一個download任務,destination表示的下載檔案的快取路徑
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
                                          destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
//繼續恢復一個download任務。resumeData參數列示的是恢復下載的時候初始化資料,比如前面已經下載好的部分資料。
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
                                                progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
                                             destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                       completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
//獲取指定Task的上傳進度
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
//獲取指定Task的下載進度
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;

注意:上面所有Task的progress都不在主執行緒、所以要在progress中做UI更新,都必須手動在主執行緒操作。

2.4 AFURLSessionManager設定各種情況的代理回撥

這些回撥Block主要是用於處理網路請求過程或者結束以後的資料處理、認證、通知、快取等。我們可以通過設定這些Block來獲取或者檢測各種狀態。相當於就是鉤子函式。通過下面的這些Block,我們基本可以獲取請求過程中的所有狀態以及需要做的各種處理。

//設定Session出錯或者無效的手的回撥Block。這個Block主要在`NSURLSessionDelegate`代理的`URLSession:didBecomeInvalidWithError:`方法中執行。
- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block{
    
}
//當網路請需要的認證資訊比如使用者名稱密碼已經傳送了的時候,就可以通過這個Block來處理。這個Block是在`NSURLSessionDelegate`代理裡面的`URLSession:didReceiveChallenge:completionHandler:`方法中被執行。注意這個是針對Session
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block{
    
}
////當網路請需要的認證資訊比如使用者名稱密碼已經傳送了的時候,就可以通過這個Block來處理。這個Block是在`NSURLSessionTaskDelegate`代理裡面的`URLSession:task:didReceiveChallenge:completionHandler:`方法中被執行。注意這個是針對Task。
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block{
    
}
//當請求需要一個新的bodystream的時候,就可以通過這個Block來設定。這個Block在`NSURLSessionTaskDelegate` 代理協議的`URLSession:task:needNewBodyStream:`方法裡面設定。
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block{
    
}
//當一個網路請求需要重定向的時候。就會呼叫這個Block。這個Block是在`NSURLSessionTaskDelegate`協議的`URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`方法中呼叫的。
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block{
    
}
//可以通過設定這個Block來獲取上傳進度。這個Block主要在`NSURLSessionTaskDelegate`協議的 `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`方法中呼叫.
- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block{
    
}
//設定一個Task完成以後執行的Block,這個Block在`NSURLSessionTaskDelegate`協議的 `URLSession:task:didCompleteWithError:`方法中執行。
- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block{
    
}
//當接收到網路請求返回以後,可以呼叫這個Block。這個Block是在`NSURLSessionDataDelegate`協議的 `URLSession:dataTask:didReceiveResponse:completionHandler:`
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block{
    
}
//如果一個dataTask轉換為downLoadTask以後,就可以設定這個Block來呼叫。在`NSURLSessionDataDelegate` 協議的`URLSession:dataTask:didBecomeDownloadTask:`方法中呼叫。
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block{
    
}
//當dataTask接收到資料以後,可以設定呼叫這個Block。具體在`NSURLSessionDataDelegate`協議的`URLSession:dataTask:didReceiveData:`方法。
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block{
    
}
//設定一個Block來決定是否處理或者換成網路請求快取。具體在`NSURLSessionDataDelegate`協議的`URLSession:dataTask:willCacheResponse:completionHandler:`方法中。
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block{
    
}
//當session所有的任務都傳送出去以後,就可以通過這個Block來獲取。具體在`NSURLSessionDataDelegate`協議的 `URLSessionDidFinishEventsForBackgroundURLSession:`方法中。
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block{
    
}
//當一個downloadTask執行完畢以後,可以通過這個Block來獲取下載資訊,我們可以通過這個Block獲取下載檔案的位置。具體在`NSURLSessionDownloadDelegate`協議的`URLSession:downloadTask:didFinishDownloadingToURL:`方法中被呼叫。
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable  (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block{
    
}
//可以通過這個Block獲取一個downloadTask的下載進度。這個Block會在下載過程中多次被呼叫。具體是在`NSURLSessionDownloadDelegate`協議中的`URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`方法中被呼叫。
- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block{
    
}
//當一個downloadTask重新開始以後,我們可以通過這個Block獲取fileOffSet等資訊獲取已經下載的部分以及總共有多少要下載。具體是在`NSURLSessionDownloadDelegate`協議的`URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`方法中被呼叫。
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block{
    
}

除了上面的部分,AFURLSessionManager的標頭檔案還提供了很多notification的宣告。通過這些通知,我們可以獲取Task是否開始、是否完成、是否掛起、是否無效等各種通知。具體可以去檔案裡看。

3 AFURLSessionManager的實現分析

AFURLSessionManager.m檔案裡面除了有AFURLSessionManager.h定義的各種介面的實現意外,還有處理不同iOS版本下NSRULSession不同的部分,以及多個全域性dispatch_queue_t的定義、以及處理NSURLSeesionTash的各種代理方法的實現和處理。具體劃分如下:

  • NSURLSessionManager的實現。主要實現了介面檔案定義的各種api的實現,比如Task的建立、Task的獲取、Task的各種代理方法的實現、NSCoping和NSCoding協議、以及各種Block的實現。

    • 基本屬性的初始化。比如sessionConfigurationoperationQueuesessionmutableTaskDelegatesKeyedByTaskIdentifier等屬性。以及用於實現task和AFURLSessionManagerTaskDelegate的繫結的taskDescriptionForSessionTasks、還有關鍵操作的鎖屬性lock。

    • 介面檔案的各種Block對應的屬性,一個Block對應一個屬性。

    • 處理Task暫停與重啟操作的方法。

    • 給Task設定AFURLSessionManagerTaskDelegate代理的方法。

    • 初始化Task的各種方法。

    • 設定B介面檔案定義的各種Block。

    • NSURLSession系列代理方法。

  • _AFURLSessionTaskSwizzling私有類。主要實現了iOS7和iOS8系統上NSURLSession差別的處理。讓不同系統版本NSURLSession版本基本一致。

  • AFURLSessionManagerTaskDelegate這個類主要是把NSURLSeesion的部分代理方法讓他處理。從而達到簡化程式碼的目的。

    • 處理Task的上傳或者下載進度。

    • 處理封裝NSURLSeesion返回的資料。

    • Task完成等的通知封裝。

  • 全域性dispatch_queue_tdispatch_group_t的定義。各種通知名稱的初始化,各種Block的型別定義。

3.1 AFURLSessionManager一個網路請求實現過程

我們通過一個網路請求過程來分析AFURLSessionManager.m的實現。我們通過initWithSessionConfiguration方法初始化一個manager。在這個方法裡會初始化各種屬性、以及為session屬性設定代理:

介面檔案中的程式碼如下:

 AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

實現檔案中對應的處理如下:

/**
 初始化方法
 @return 返回一個manager物件
 */
- (instancetype)init {
    return [self initWithSessionConfiguration:nil];
}
/**
 預設初始化方法、通過這個方法來做manager的具體化初始化動作

 @param configuration NSURLSession的配置
 @return 返回一個manager物件
 */
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    //如果使用者沒有手動指定,則使用預設的configuration來初始化
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    //賦值給屬性
    self.sessionConfiguration = configuration;
    //初始化NSURLSession的task代理方法執行的佇列。
    //這裡有一個很關鍵的點是task的代理執行的queque一次性只能執行一個task。這樣就避免了task的代理方法執行的混亂。
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;
    //出絲滑NSURLSession物件,最核心的物件。
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    //如果使用者沒有手動指定,則返回的資料是JSON格式序列化。
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    //指定https處理的安全策略。
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
    //初始化網路狀態監聽屬性
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
    //用於記錄Task與他的`AFURLSessionManagerTaskDelegate`代理物件的一一對應關係。通過這個
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    //初始化一個鎖物件,關鍵操作加鎖。
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;
    /**
     獲取當前session正在執行的所有Task。同時為每一個Task新增`AFURLSessionManagerTaskDelegate`代理物件,這個代理物件主要用於管理uplaodTak和downloadTask的進度管理。並且在Task執行完畢以後呼叫相應的Block。同時傳送相應的notification物件,實現對task資料或者狀態改變的檢測。
     @param dataTasks dataTask列表
     @param uploadTasks uplaodTask列表
     @param downloadTasks downloadTask列表
     @return
     */
    [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;
}

請求執行,介面檔案如下:

NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
    NSLog(@"下載進度:%lld",downloadProgress.completedUnitCount);
} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
    NSLog(@"fileURL:%@",[fileURL absoluteString]);
    return fileURL;
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:filePath]];
    NSLog(@"File downloaded to: %@", filePath);
}];

實現檔案則呼叫了很多方法:

1 首先是初始化一個NSURLSessionDownLoadTask物件

    //通過session建立一個downloadTask,
    __block NSURLSessionDownloadTask *downloadTask = nil;
    //url_session_manager_create_task_safely作用是修復在iOS8下面的系統bug。
    url_session_manager_create_task_safely(^{
        downloadTask = [self.session downloadTaskWithRequest:request];
    });
    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
    return downloadTask;

2 通過[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];這句話來為Task設定一個AFURLSessionManagerTaskDelegate代理物件。從而可以實現對進度處理、Block呼叫、Task完成返回資料的拼裝的功能。

    //根據指定的Task,初始化一個AFURLSessionManagerTaskDelegate
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
    delegate.manager = self;
    //設定Task完成的回撥Block
    delegate.completionHandler = completionHandler;
    if (destination) {
        //任務完成以後,呼叫destination這個Block
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
            return destination(location, task.response);
        };
    }
    //指定Task與taskDescriptionForSessionTasks的關聯關係,方便後面的通知中做對應的處理。
    downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
    //新增通知
    [self setDelegate:delegate forTask:downloadTask];
    //設定一個下載進度的Block,以便在後面代理方法中呼叫。
    delegate.downloadProgressBlock = downloadProgressBlock;

3 初始化一個AFURLSessionManagerTaskDelegate物件。在這個物件中對Task的請求過程進行處理和控制。

/**
 初始化一個AFURLSessionManagerTaskDelegate物件
 @param task 物件繫結的Task
 @return 返回物件
 */
- (instancetype)initWithTask:(NSURLSessionTask *)task {
    self = [super init];
    if (!self) {
        return nil;
    }
    //這個屬性用於儲存Task下載過程中的資料
    _mutableData = [NSMutableData data];
    //儲存Task上傳和下載的進度
    _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
        progress.cancellationHandler = ^{
            [weakTask cancel];
        };
        progress.pausable = YES;
        progress.pausingHandler = ^{
            //掛起Task
            [weakTask suspend];
        };
        if ([progress respondsToSelector:@selector(setResumingHandler:)]) {
            progress.resumingHandler = ^{
                //重啟Task
                [weakTask resume];
            };
        }
        //更具progress的進度來獲取Task的進度。fractionCompleted方法在請求過程中多次執行。
        [progress addObserver:self
                   forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                      options:NSKeyValueObservingOptionNew
                      context:NULL];
    }
    return self;
}
//上面通過對fractionCompleted方法KVO。則會呼叫下面的方法,從而執行manager的
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
   if ([object isEqual:self.downloadProgress]) {
       //更新下載進度Block
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }else if ([object isEqual:self.uploadProgress]) {
        //更新上傳進度Bloc
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

4 在AFURLSessionManagerTaskDelegate設定Task狀態改變的監聽。

/**
 設定指定task的`AFURLSessionManagerTaskDelegate`物件。並且新增task掛起或者重啟的監聽。
 @param delegate 代理物件
 @param task task
 */
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);
    //加鎖操作
    [self.lock lock];
    //為Task設定與之代理方法關聯關係。通過一個字典
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    //新增對Task開始、重啟、掛起狀態的通知的接收。
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}
/**
 給Task新增任務開始、重啟、掛起的通知

 @param task 任務
 */
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}

5 從下面開始,任務就正式開始執行。其實就是[downloadTask resume];執行以後開始。

/**
 在網路請求正式開始以後,這個方法會在資料接收的過程中多次呼叫。我們可以通過這個方法獲取資料下載的大小、總得大小、還有多少麼有下載
 @param session session
 @param downloadTask 對應的Task
 @param bytesWritten 已經下載的位元組
 @param totalBytesWritten 總的位元組大小
 @param totalBytesExpectedToWrite nil
 */
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    //獲取Task對應的`AFURLSessionManagerTaskDelegate`物件。從而可以呼叫對應的代理方法
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
    if (delegate) {
        //呼叫`AFURLSessionManagerTaskDelegate`類中的代理方法。從而實現對於進度更新等功能。
        //會呼叫下面的那個方法
        [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
    }
    if (self.downloadTaskDidWriteData) {
        //如果有`downloadTaskDidWriteData`Block的實現,則在這個呼叫Block從而實現對下載進度過程的控制。
        self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    }
}
//AFURLSessionManagerTaskDelegate裡面的這個代理方法實現對進度的更新。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    //AFURLSessionManagerTaskDelegate代理方法實現對下載進度的記錄
    self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
    self.downloadProgress.completedUnitCount = totalBytesWritten;
}

6 Task完成以後,會呼叫AFURLSessionManagerTaskDelegate物件的方法對返回的資料封裝。

//AFURLSessionManagerTaskDelegate裡面的這個代理方法實現對資料的具體處理。
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    //獲取Task對應的manager物件
    __strong AFURLSessionManager *manager = self.manager;
    //要封裝的responseObject物件。
    __block id responseObject = nil;
    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    //返回的資料。
    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;
    }
    //如果是downloadTask,則封裝downloadFileURL
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {//如果是其他Task,則封裝返回的data。
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }
    //有錯封裝
    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            //如果Task有completionHandler。則呼叫這個Block
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }
            //傳送一個指定Task結束的通知
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {//正確資料封裝
        //在一個並行的dispat_queuq_t物件裡面非同步處理。
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            //封裝responseBojct
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }
            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }
            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                //如果Task有完成Block。則呼叫這個Block
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }
                //傳送通知
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}

7 移除Task對應的通知和對應的AFURLSessionManagerTaskDelegate代理物件。

- (void)removeDelegateForTask:(NSURLSessionTask *)task {
    NSParameterAssert(task);
    [self.lock lock];
    //移除Task對應的通知
    [self removeNotificationObserverForTask:task];
    //移除Task對應的`AFURLSessionManagerTaskDelegate`代理物件。
    [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
    [self.lock unlock];
}
//移除通知監聽
- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task];
}
//`AFURLSessionManagerTaskDelegate`物件回收。
- (void)dealloc {
    [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}

通過上面的過程,我們發現核心流程都是圍繞了NSRULSessionTask物件以及與之繫結的AFURLSessionManagerTaskDelegate物件執行的。我們通過在NSRULSessionTask物件的代理方法裡面手動呼叫AFURLSessionManagerTaskDelegate對應的代理方法來實現對資料的處理和簡化程式碼的作用,這個設計思路的確吊吊的。還有一些方法沒有涉及到,不過大同小異,基本過程就是這樣,就不一一解釋了。

3.2 AFURLSessionManager一些特殊模組的說明

AFURLSeeesionManager實現了NSSecureCoding協議。讓manager可以歸檔解檔。

/**
 在iOS8以及以上環境下,supportsSecureCoding必須重寫並且返回true。
 @return bool
 */
+ (BOOL)supportsSecureCoding {
    return YES;
}
//解檔
- (instancetype)initWithCoder:(NSCoder *)decoder {
    NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];
    self = [self initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }
    return self;
}
/**
 我們發現物件歸檔的時候,只歸檔了`NSURLSessionConfiguration`屬性。所以說歸檔接檔的時候所有Block設定、operation設定都會失效。
 @param coder coder
 */
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
}

同時,AFURLSessionManager也實現了NSCopying協議。通過協議的實現過程,我們發現也是隻使用了NSURLSessionConfiguration屬性。和歸檔解檔一樣。

#pragma mark - 實現NSCopying協議。copy的NAURLSessionManager沒有複製任何與代理處理相關的Block
- (instancetype)copyWithZone:(NSZone *)zone {
    return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];
}

有的時候,我們的請求會返回302這個狀態碼,這個表示需要請求重定向到另一個url,我們可以下面這個代理方法裡面決定對於重定向的處理,如果對completionHandler傳入nil,則會把response傳入重定向請求。另外,backgroundSession的Task不會呼叫下面這個代理方法,而是直接呼叫。

/**
 有的時候,我們的請求會返回302這個狀態碼,這個表示需要請求重定向到另一個url,我們可以在這個代理方法裡面絕定對於重定向的處理。
 @param session session
 @param task task
 @param response response
 @param request 重定向的request。
 @param completionHandler 請求完成
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
{
    //重定向的request物件
    NSURLRequest *redirectRequest = request;
    //如果使用者指定了taskWillPerformHTTPRedirection這個Block,我們就通過這個Block的呼叫返回處理完成的request物件。
    if (self.taskWillPerformHTTPRedirection) {
        redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }
    //這個呼叫是必須的,執行重定向操作。
    if (completionHandler) {
        completionHandler(redirectRequest);
    }
}

建立NSRULSessionUplaodTask的時候,在某些系統上會出現bug。AFN已經幫我們處理好:

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionUploadTask *uploadTask = nil;
    //用執行緒安全的方式建立一個dataTask。修復iOS8下面的bug。
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
    });
    //用於處理uploadTask在iOS7環境下面有可能建立失敗的情況。如果attemptsToRecreateUploadTasksForBackgroundSessions為true。則嘗試重新建立Task。如果三次都沒有成功,則放棄。
    if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
        for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
            uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
        }
    }
    //為Task新增`AFURLSessionManagerTaskDelegate`代理方法
    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
    return uploadTask;
}

通過使用dispatch_semaphore_t來控制對非同步處理返回結果的控制。非常有借鑑意義。

#pragma mark -  獲取當前session對應的task列表。通過dispatch_semaphore_t來控制訪問過程。
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    __block NSArray *tasks = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
            tasks = dataTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
            tasks = uploadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
            tasks = downloadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
            tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
        }
        //這裡傳送一個訊號量,讓semaphore變為1。此時表示tasks已經成功獲取。
        dispatch_semaphore_signal(semaphore);
    }];
    //這裡會一直等待訊號量變為1。
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    //返回Task。通過訊號量控制,避免了方法結束的時候,tasks還沒有正常獲取的情況。
    return tasks;
}

4 _AFURLSessionTaskSwizzling私有類的說明

在iOS7和iOS8及以上的系統,NSRULSessionTask的具體實現是不同的。我們目前知道的不同有:

  • NSURLSessionTasks是一個類簇。所以我們初始化一個Task的時候,我們並不只到初始化的到底是哪個子類。

  • 簡單的通過[NSURLSessionTask class]並不會起作用。必須通過NSURLSession建立一個task物件。然後獲取他所在的類。

  • iOS7下面,下面程式碼中的localDataTask物件的繼承關係是__NSCFLocalDataTask -> __NSCFLocalSessionTask -> __NSCFURLSessionTask

  • 在iOS8以及以上系統。下面程式碼中的localDataTask物件的繼承關係是__NSCFLocalDataTask -> __NSCFLocalSessionTask -> NSURLSessionTask

  • 在iOS7下面__NSCFLocalSessionTask__NSCFURLSessionTask實現了resumesuspend方法,同時最重要的是他不呼叫父類的實現。但是iOS8下面,只有NSURLSessionTask實現了resumesuspend。所以在iOS7的環境下,我們需要想辦法讓resumesuspend呼叫NSURLSessionTask的具體實現。

下面的程式碼完美的向我們展示了一個向類新增方法,並且swizzle方法實現的過程。值得仔細琢磨。

/**
 切換theClass類的`originalSelector`和`swizzledSelector`的實現
 @param theClass 類
 @param originalSelector 方法一
 @param swizzledSelector 方法2
 */
static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
    Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}
/**
 動態給一個類新增方法
 @param theClass 類
 @param selector 方法名字
 @param method 方法體
 @return bool
 */
static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
    return class_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));
}
@implementation _AFURLSessionTaskSwizzling
+ (void)load {
    if (NSClassFromString(@"NSURLSessionTask")) {
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
        //初始化一個dataTask物件
        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
        //獲取af_resume這個方法的實現。
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        //獲取dataTask的具體類
        Class currentClass = [localDataTask class];
        //如果父類有resume方法。則改變方法的具體實現。
        while (class_getInstanceMethod(currentClass, @selector(resume))) {
            Class superClass = [currentClass superclass];
            //找到類和父類的resume方法實現
            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                //新增方法、然後轉換方法的實現
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
            currentClass = [currentClass superclass];
        }
        [localDataTask cancel];
        [session finishTasksAndInvalidate];
    }
}
/**
 主要是實現了為一個類新增方法、並且轉換新增方法和原來對應方法的實現。
 @param theClass 要操作的類
 */
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
    Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
    Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
    //為theClass類新增一個af_resume方法。
    if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
        //把dataTask的resume和afresume方法的實現互換。
        af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
    }
    //為theClass類新增一個af_suspend方法
    if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
        //把dataTask的suspend和af_suspend方法的實現互換。
        af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
    }
}
- (NSURLSessionTaskState)state {
    NSAssert(NO, @"State method should never be called in the actual dummy class");
    return NSURLSessionTaskStateCanceling;
}
/**
 在iOS7下面,`NSURLSessionDataTask`呼叫resume方法其實就是執行`af_resume`的具體實現。
 */
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    //這裡其實就是呼叫dataTask的resume實現
    [self af_resume];
    if (state != NSURLSessionTaskStateRunning) {
        //這裡的self其實就是`NSRULSessionDataTask`物件
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}
/**
 在iOS7下面,`NSURLSessionDataTask`呼叫suspend方法其實就是執行`af_suspend`的具體實現。
 */
- (void)af_suspend {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    //這裡其實就是呼叫dataTask的suspend具體實現
    [self af_suspend];
    if (state != NSURLSessionTaskStateSuspended) {
        //這裡的self其實就是`NSRULSessionDataTask`物件
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}
@end

5 總結

AFURLSessionManager通過對task設定一個AFURLSessionManagerTaskDelegate代理來處理繁雜的請求進度管理。從而降低了程式碼的負責度。是代理模式的一個很好的實踐。

AFURLSessionManager通過私有類_AFURLSessionTaskSwizzling來修改iOS7和iOS8系統上面不同。是對於方法swizzle的一個成功和完整的實踐。

AFURLSessionManager通過新增各種Block,讓我們對請求過程有全方位的控制和處理。同時提供簡潔的api,把負責的處理全部封裝好。

原始碼地址iOSSourceCodeStudy,部落格地址部落格地址

相關文章