AFNetworking 3.0 源碼閱讀筆記(三)

原文鏈接:http://itangqi.me/2016/05/09/the-notes-of-learning-afnetworking-three/

前言

AFURLSessionManager 絕對可以稱得上是 AFNetworking 的核心,所以本文篇幅會相對長一點,但我保證絕對是滿滿的乾貨~


AFURLSessionManager

首先,在 AFURLSessionManager.h 中關於 AFURLSessionManager 的概述:

AFURLSessionManager creates and manages an NSURLSession object based on a specified NSURLSessionConfiguration object, which conforms to <NSURLSessionTaskDelegate>, <NSURLSessionDataDelegate>, <NSURLSessionDownloadDelegate>, and <NSURLSessionDelegate>.

最終可以歸結爲以下幾點:

  1. 負責創建和管理 NSURLSession
  2. 管理 NSURLSessionTask
  3. 實現 NSURLSessionDelegate 等協議中的代理方法
  4. 使用 AFURLSessionManagerTaskDelegate 管理進度
  5. 使用 _AFURLSessionTaskSwizzling 調劑方法
  6. 引入 AFSecurityPolicy 保證請求的安全
  7. 引入 AFNetworkReachabilityManager 監控網絡狀態

創建和管理 NSURLSession

按慣例,我們由 AFURLSessionManager 的初始化方法:- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; 進行展開:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    self.sessionConfiguration = configuration;

    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    self.responseSerializer = [AFJSONResponseSerializer serializer];

    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif

    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

    [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;
}

該方法主要完成如下工作:

  1. 初始化會話配置(NSURLSessionConfiguration),默認爲 defaultSessionConfiguration
  2. 設置相應的 OperationQueue,決定請求過程中的一系列事件在哪個 OperationQueue 回調,這裏是設置了最大併發量爲 1 的隊列,也就相當於串行隊列了。(AFNetworing 2.0 版本是設置了一條常駐線程來響應所有網絡請求的 delegate 事件)
  3. 初始化會話(session),並設置會話的代理及代理隊列,delegate 用來處理請求中的各種事件,可以設置爲 nil 使用系統提供的 delegate,但是要想支持後臺傳輸數據必須提供自定義實現的 delegate;另外,NSURLSession 對象是強引用了 delegate,如果程序最終沒有調用 invalidateAndCancel 方法來 invalidate 該 session 的話,則會造成內存泄漏
  4. 初始化管理響應序列化(AFJSONResponseSerializer),安全認證(AFSecurityPolicy)以及監控網絡狀態(AFNetworkReachabilityManager)的實例
  5. 初始化保存 data task 的字典(mutableTaskDelegatesKeyedByTaskIdentifier)

管理 NSURLSessionTask

接下來,在獲得了 AFURLSessionManager 的實例之後,我們可以通過以下方法創建 NSURLSessionDataTask 的實例:

1
2
3
4
5
6
7
8
9
10
11
12
/**
 Creates an `NSURLSessionDataTask` with the specified request.

 @param request The HTTP request for the request.
 @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
 */
- (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;

...

這裏省略了一些返回 NSURLSessionTask 的方法,因爲這些接口的形式都是差不多的。

擴展Difference between nullable, __nullable and _Nullable in Objective-C

下面,我們將以 - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:] 方法的實現爲例,分析它是如何實例化並返回一個 NSURLSessionTask 的實例的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (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;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

該方法主要完成如下工作:

  1. 調用 - [NSURLSession dataTaskWithRequest:] 方法傳入 NSURLRequest
  2. 調用 - [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:] 方法返回一個 AFURLSessionManagerTaskDelegate 對象
  3. completionHandler uploadProgressBlockdownloadProgressBlock 傳入該對象並在相應事件發生時進行回調

url_session_manager_create_task_safely 的調用是因爲蘋果框架中的一個 bug #2093,如果有興趣可以看一下,在這裏就不具體說明了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (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] init];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

在這個方法中同時調用了另一個方法 - [AFURLSessionManager setDelegate:forTask:] 來設置代理:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{

    #1: 檢查參數, 略

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

正如上面所提到的,AFURLSessionManager 就是通過字典 mutableTaskDelegatesKeyedByTaskIdentifier 來存儲並管理每一個 NSURLSessionTask,它以 taskIdentifier 爲鍵存儲 task。

該方法使用 NSLock 來保證不同線程使用 mutableTaskDelegatesKeyedByTaskIdentifier 時,不會出現線程競爭的問題(既線程同步)。

同時調用 - setupProgressForTask:,我們會在下面具體介紹這個方法。


實現 NSURLSessionDelegate 等協議中的代理方法

首先,NSURLSession 的代理對象結構如下:

接下來,我們來看下具體的代理方法:

  • NSURLSessionDelegate
  • NSURLSessionTaskDelegate,遵守 NSURLSessionDelegate 協議
  • NSURLSessionDataDelegate,遵守 NSURLSessionTaskDelegate 協議,是網絡請求通常遵循的協議,常用的方法:

    • 接受到服務響應時調用的方法

      1
      2
      3
      4
      5
      
      - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
      /**
      * 必須在該方法中對服務器的響應進行授權,才能繼續接收服務器返回的數據,調用如下函數
      * completionHandler(NSURLSessionResponseAllow)
      */
      
    • 接收到服務器返回的數據時調用的方法

      1
      2
      3
      4
      
      - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
      /**
      * data:服務返回的數據,通常爲 JSON 格式數據
      */
      
    • 請求完成時調用的方法(成功或失敗)

      1
      2
      3
      4
      
      - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
      /**
      * 若出現錯誤,error 中存放錯誤信息
      */
      
  • NSURLSessionDownloadDelegate(通常用於下載大量數據),遵守 NSURLSessionTaskDelegate 協議,常用的方法:

    • 寫入數據到臨時文件時調用的方法(服務器返回一點就寫入一點)

      1
      2
      3
      4
      5
      6
      
      - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:	(int64_t)totalBytesExpectedToWrite
      /**
      * totalBytesWritten,已寫入數據的總長度
      * totalBytesExpectedToWrite:總共要寫入數據的總長度
      * 可以在該方法中計算下載進度
      */
      
    • 遇到錯誤的時候調用

      1
      2
      3
      4
      
      - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
      /**
      *error:若遇到錯誤,則保存錯誤信息
      */
      
    • 用於斷點下載的方法

      1
      2
      3
      4
      5
      
      - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
      /**
      * fileOffset:繼續下載時,文件的開始位置
      * expectedTotalBytes:剩餘的數據總數
      */
      
    • 下載完成時調用的方法

      1
      2
      3
      4
      5
      
      - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
      /**
      * location:下載的文件保存的臨時位置
      * 需要將下載的文件保存在可以長期保存的位置
      */
      

使用 AFURLSessionManagerTaskDelegate 管理進度

在上面我們提到過 AFURLSessionManagerTaskDelegate 類,它主要爲 task 提供進度管理功能,並在 task 結束時回調, 也就是調用在 - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:] 等方法中傳入的 completionHandler

我們首先分析一下 AFURLSessionManagerTaskDelegate 是如何對進度進行跟蹤的:

1
2
3
4
5
6
7
- (void)setupProgressForTask:(NSURLSessionTask *)task {

    #1:設置在上傳進度或者下載進度狀態改變時的回調
true
    #2:KVO

}

該方法的實現有兩個部分,一部分是對代理持有的兩個屬性 uploadProgressdownloadProgress 設置回調

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__weak __typeof__(task) weakTask = task;

self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
[self.uploadProgress setCancellable:YES];
[self.uploadProgress setCancellationHandler:^{
   __typeof__(weakTask) strongTask = weakTask;
   [strongTask cancel];
}];
[self.uploadProgress setPausable:YES];
[self.uploadProgress setPausingHandler:^{
   __typeof__(weakTask) strongTask = weakTask;
   [strongTask suspend];
}];
if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
   [self.uploadProgress setResumingHandler:^{
       __typeof__(weakTask) strongTask = weakTask;
       [strongTask resume];
   }];
}

這裏只有對 uploadProgress 設置回調的代碼,設置 downloadProgress 與這裏完全相同

主要目的是在對應 NSProgress 的狀態改變時,調用 resume suspend 等方法改變 task 的狀態。

第二部分是對 task 和 NSProgress 屬性進行鍵值觀測:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[task addObserver:self
      forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
         options:NSKeyValueObservingOptionNew
         context:NULL];
[task addObserver:self
      forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
         options:NSKeyValueObservingOptionNew
         context:NULL];

[task addObserver:self
      forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
         options:NSKeyValueObservingOptionNew
         context:NULL];
[task addObserver:self
      forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
         options:NSKeyValueObservingOptionNew
         context:NULL];

[self.downloadProgress addObserver:self
                       forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                          options:NSKeyValueObservingOptionNew
                          context:NULL];
[self.uploadProgress addObserver:self
                     forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                        options:NSKeyValueObservingOptionNew
                        context:NULL];

observeValueForKeypath:ofObject:change:context: 方法中改變進度,並調用 block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([object isKindOfClass:[NSURLSessionTask class]]) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
            self.downloadProgress.completedUnitCount = [change[@"new"] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
            self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
            self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue];
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
            self.uploadProgress.totalUnitCount = [change[@"new"] longLongValue];
        }
    }
    else if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

對象的某些屬性改變時更新 NSProgress 對象或使用 block 傳遞 NSProgress 對象 self.uploadProgressBlock(object)

代理方法 URLSession:task:didCompleteWithError:

在每一個 NSURLSessionTask 結束時,都會在代理方法 URLSession:task:didCompleteWithError: 中:

  1. 調用傳入的 completionHander block
  2. 發出 AFNetworkingTaskDidCompleteNotification 通知
1
2
3
4
5
6
7
8
9
10
11
12
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    #1:獲取數據, 存儲 `responseSerializer` 和 `downloadFileURL`

    if (error) {
    	#2:在存在錯誤時調用 `completionHandler`
    } else {
truetrue#3:調用 `completionHandler`
    }
}

這是整個代理方法的骨架,先看一下最簡單的第一部分代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

// 具體可以查看 #issue 2672。這裏主要是針對大文件的時候,性能提升會很明顯
NSData *data = nil;
if (self.mutableData) {
   data = [self.mutableData copy];
   // 此處不再需要 mutableData 了
   self.mutableData = nil;
}

if (self.downloadFileURL) {
   userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
   userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}

再來看第二部分:這部分代碼從 mutableData 中取出了數據,設置了 userInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
// 如果 task 出錯了,處理 error 信息
// 所以對應的觀察者在處理 error 的時候,比如可以先判斷 userInfo[AFNetworkingTaskDidCompleteErrorKey] 是否有值,有值的話,就說明是要處理 error
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
    if (self.completionHandler) {
        self.completionHandler(task.response, responseObject, error);
    }

    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
    });
});

如果當前 manager 持有 completionGroup 或者 completionQueue 就使用它們。否則會創建一個 dispatch_group_t 並在主線程中調用 completionHandler 併發送通知(在主線程中)。

最後一部分:如果在執行當前 task 時沒有遇到錯誤,那麼先對數據進行序列化,然後同樣調用 block 併發送通知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
dispatch_async(url_session_manager_processing_queue(), ^{
    NSError *serializationError = nil;
    // 根據對應的 task 和 data 將 response data 解析成可用的數據格式,比如 JSON serializer 就將 data 解析成 JSON 格式
    responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
    // 注意如果有 downloadFileURL,意味着 data 存放在了磁盤上了,所以此處 responseObject 保存的是 data 存放位置,供後面 completionHandler 處理。沒有 downloadFileURL,就直接使用內存中的解析後的 data 數據
    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(), ^{
        if (self.completionHandler) {
            self.completionHandler(task.response, responseObject, serializationError);
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
        });
    });
});

代理方法 URLSession:dataTask:didReceiveData:- URLSession:downloadTask:didFinishDownloadingToURL:

這兩個代理方法分別會在收到數據或者完成下載對應文件時調用,作用分別是爲 mutableData 追加數據和處理下載的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- (void)URLSession:(__unused NSURLSession *)session
          dataTask:(__unused NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    [self.mutableData appendData:data];
}

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    NSError *fileManagerError = nil;
    self.downloadFileURL = nil;

    if (self.downloadTaskDidFinishDownloading) {
        self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        if (self.downloadFileURL) {
            [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];

            if (fileManagerError) {
                [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
            }
        }
    }
}

使用 _AFURLSessionTaskSwizzling 調劑方法

_AFURLSessionTaskSwizzling 的唯一功能就是修改 NSURLSessionTaskresumesuspend 方法,使用下面的方法替換原有的實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];
    
    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}

- (void)af_suspend {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_suspend];
    
    if (state != NSURLSessionTaskStateSuspended) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}

這樣做的目的是爲了在方法 resume 或者 suspend 被調用時發出通知。

具體方法調劑的過程是在 + load 方法中進行的

load 方法只會在整個文件被引入時調用一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
+ (void)load {
    if (NSClassFromString(@"NSURLSessionTask")) {
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        // 首先構建一個 NSURLSession 對象 session,再通過 session 構建出一個 _NSCFLocalDataTask 變量
        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
        // 獲取到 af_resume 實現的指針
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        Class currentClass = [localDataTask class];
        // 檢查當前 class 是否實現了 resume。如果實現了,繼續第 4 步
        while (class_getInstanceMethod(currentClass, @selector(resume))) {
            // 獲取到當前 class 的父類(superClass)
            Class superClass = [currentClass superclass];
            // 獲取到當前 class 對於 resume 實現的指針
            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
            // 獲取到父類對於 resume 實現的指針
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
            // 如果當前 class 對於 resume 的實現和父類不一樣(類似 iOS 7 上的情況),並且當前 class 的 resume 實現和 af_resume 不一樣,才進行 method swizzling
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
            // 設置當前操作的 class 爲其父類 class,重複步驟 3~8
            currentClass = [currentClass superclass];
        }
        
        [localDataTask cancel];
        [session finishTasksAndInvalidate];
    }
}
  1. 首先用 NSClassFromString(@"NSURLSessionTask") 判斷當前部署的 iOS 版本是否含有類 NSURLSessionTask
  2. 因爲 iOS 7 和 iOS 8 上對於 NSURLSessionTask 的實現不同,所以會通過 - [NSURLSession dataTaskWithURL:] 方法返回一個 NSURLSessionTask 實例
  3. 取得當前類 _AFURLSessionTaskSwizzling 中的實現 af_resume
  4. 如果當前類 currentClassresume 方法
    • 真:5
    • 假:6
  5. 使用 swizzleResumeAndSuspendMethodForClass: 調劑該類的 resumesuspend 方法
  6. currentClass = [currentClass superclass]

這裏複雜的實現是爲了解決 bug #2702


引入 AFSecurityPolicy 保證請求的安全

AFSecurityPolicyAFNetworking 用來保證 HTTP 請求安全的類,它被 AFURLSessionManager 持有,如果你在 AFURLSessionManager 的實現文件中搜索 self.securityPolicy,你只會得到三條結果:

  1. 初始化 self.securityPolicy = [AFSecurityPolicy defaultPolicy]
  2. 收到連接層的驗證請求時
  3. 任務接收到驗證請求時

在 API 調用上,後兩者都調用了 - [AFSecurityPolicy evaluateServerTrust:forDomain:] 方法來判斷當前服務器是否被信任,我們會在接下來的文章中具體介紹這個方法的實現的作用。


引入 AFNetworkReachabilityManager 監控網絡狀態

AFSecurityPolicy 相同,AFURLSessionManager 對網絡狀態的監控是由 AFNetworkReachabilityManager 來負責的,它僅僅是持有一個 AFNetworkReachabilityManager 的對象。

真正需要判斷網絡狀態時,仍然需要開發者調用對應的 API 獲取網絡狀態


小結

  • AFURLSessionManager 是對 NSURLSession 的封裝
  • 它通過 - [AFURLSessionManager dataTaskWithRequest:completionHandler:] 等接口創建 NSURLSessionDataTask 的實例
  • 持有一個字典 mutableTaskDelegatesKeyedByTaskIdentifier 管理這些 data task 實例
  • 引入 AFURLSessionManagerTaskDelegate 來對傳入的 uploadProgressBlock downloadProgressBlock completionHandler 在合適的時間進行調用
  • 實現了全部的代理方法來提供 block 接口
  • 通過方法調劑在 data task 狀態改變時,發出通知

參考

發佈了24 篇原創文章 · 獲贊 19 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章