原文鏈接:http://itangqi.me/2016/05/09/the-notes-of-learning-afnetworking-three/
前言
AFURLSessionManager
絕對可以稱得上是 AFNetworking 的核心,所以本文篇幅會相對長一點,但我保證絕對是滿滿的乾貨~
AFURLSessionManager
首先,在 AFURLSessionManager.h
中關於 AFURLSessionManager
的概述:
AFURLSessionManager
creates and manages anNSURLSession
object based on a specifiedNSURLSessionConfiguration
object, which conforms to<NSURLSessionTaskDelegate>
,<NSURLSessionDataDelegate>
,<NSURLSessionDownloadDelegate>
, and<NSURLSessionDelegate>
.
最終可以歸結爲以下幾點:
- 負責創建和管理
NSURLSession
- 管理
NSURLSessionTask
- 實現
NSURLSessionDelegate
等協議中的代理方法 - 使用
AFURLSessionManagerTaskDelegate
管理進度 - 使用
_AFURLSessionTaskSwizzling
調劑方法 - 引入
AFSecurityPolicy
保證請求的安全 - 引入
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; } |
該方法主要完成如下工作:
- 初始化會話配置(NSURLSessionConfiguration),默認爲
defaultSessionConfiguration
- 設置相應的
OperationQueue
,決定請求過程中的一系列事件在哪個OperationQueue
回調,這裏是設置了最大併發量爲 1 的隊列,也就相當於串行隊列了。(AFNetworing 2.0 版本是設置了一條常駐線程來響應所有網絡請求的 delegate 事件) - 初始化會話(session),並設置會話的代理及代理隊列,delegate 用來處理請求中的各種事件,可以設置爲 nil 使用系統提供的 delegate,但是要想支持後臺傳輸數據必須提供自定義實現的 delegate;另外,
NSURLSession
對象是強引用了 delegate,如果程序最終沒有調用invalidateAndCancel
方法來 invalidate 該 session 的話,則會造成內存泄漏 - 初始化管理響應序列化(AFJSONResponseSerializer),安全認證(AFSecurityPolicy)以及監控網絡狀態(AFNetworkReachabilityManager)的實例
- 初始化保存 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; } |
該方法主要完成如下工作:
- 調用
- [NSURLSession dataTaskWithRequest:]
方法傳入NSURLRequest
- 調用
- [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:]
方法返回一個AFURLSessionManagerTaskDelegate
對象 - 將
completionHandler
uploadProgressBlock
和downloadProgressBlock
傳入該對象並在相應事件發生時進行回調
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 } |
該方法的實現有兩個部分,一部分是對代理持有的兩個屬性 uploadProgress
和 downloadProgress
設置回調
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:
中:
- 調用傳入的
completionHander
block - 發出
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
的唯一功能就是修改 NSURLSessionTask
的
resume
和 suspend
方法,使用下面的方法替換原有的實現:
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]; } } |
- 首先用
NSClassFromString(@"NSURLSessionTask")
判斷當前部署的 iOS 版本是否含有類NSURLSessionTask
- 因爲 iOS 7 和 iOS 8 上對於
NSURLSessionTask
的實現不同,所以會通過- [NSURLSession dataTaskWithURL:]
方法返回一個NSURLSessionTask
實例 - 取得當前類
_AFURLSessionTaskSwizzling
中的實現af_resume
- 如果當前類
currentClass
有resume
方法- 真:5
- 假:6
- 使用
swizzleResumeAndSuspendMethodForClass:
調劑該類的resume
和suspend
方法 currentClass = [currentClass superclass]
這裏複雜的實現是爲了解決 bug #2702
引入 AFSecurityPolicy
保證請求的安全
AFSecurityPolicy
是 AFNetworking
用來保證 HTTP 請求安全的類,它被
AFURLSessionManager
持有,如果你在 AFURLSessionManager
的實現文件中搜索
self.securityPolicy,你只會得到三條結果:
- 初始化
self.securityPolicy = [AFSecurityPolicy defaultPolicy]
- 收到連接層的驗證請求時
- 任務接收到驗證請求時
在 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 狀態改變時,發出通知