AFNetworking 是 Objective-C 中用于網絡請求的第三方框架,我們一般使用它來封裝網絡請求,這篇文章記錄了閱讀 AFNetworking(Version 3.1.0) 源碼的筆記,簡單的研究了它的實現細節。
前言
這里引用 @draveness 的一張圖來展示 AFNetworking 的整個架構:
本系列以此架構為依據,隨后的文章會逐一分析 AFNetworking 的實現原理。
流程
首先以官方 Demo 中的一個 Get 方法為例,來探究 AFNetworking 網絡請求的大致流程。
- 1.Get 請求入口
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
- 2.創建 NSURLSessionDataTask
- (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
- 3.創建 NSURLSessionDataTask 前先創建 NSMutableURLRequest
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
- 4.用 request 創建 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
- 5.給 dataTask 添加 Delegate (主要為 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
以上方法是調用鏈上的主要方法,將在下文逐一解釋。
1.Get 請求入口
具體方法體如下:
// 創建這個 NSURLSessionDataTask
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
/**
* session task的幾種狀態的操作函數
suspend -- 可以讓當前的任務暫停
resume ---- 方法不僅可以啟動任務,還可以喚醒suspend狀態的任務
cancel ----- 方法可以取消當前的任務,你也可以向處于suspend狀態的任務發送cancel消息,任務如果被取消便不能再恢復到之前的狀態.
*/
[dataTask resume];
使用 GET 類型的 Request 來創建并運行一個 NSURLSessionDataTask,這里要注意的是用[dataTask resume]
來開啟這個 task。
2.創建 NSURLSessionDataTask
主要方法體如下:
...
// 創建 NSMutableURLRequest
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
// 處理構建 request 產生的錯誤
...
// 創建 dataTask
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
dataTaskWithHTTPMethod:
這個函數的實現主要分兩部分,一部分是構建NSMutableURLRequest,另一部分是根據已構建好的 Request 來創建 DataTask。
其中 Request 是由傳遞的 method 來創建,這里傳遞的是 GET,其它參數還有HEAD、POST、PUT、PATCH、DELETE 。
3.創建 NSMutableURLRequest
使用指定的 HTTP method 和 URLString 來構建一個 NSMutableURLRequest 對象實例,主要方法體如下:
// 參數斷言
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
// 使用url構建并初始化NSMutableURLRequest,然后設置HTTPMethod
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 給NSMutableURLRequest自帶的屬性賦值
// 然后通過判斷mutableObservedChangedKeyPaths(NSMutableSet)中是否有這個keyPath,來設定mutableRequest對應的keyPath值
// AFHTTPRequestSerializerObservedKeyPaths這個數組里的屬性是固定的,且在 init 方法里全都 KVO 了
/**
* 當 AFHTTPRequestSerializerObserverContext 中有 value 變化了(且變化后的新值不為 NSNull null),就會響應 observerValueForKeyPath 這個函數,從而mutableObservedChangedKeyPaths就會添加這個 keyPath
*/
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 將傳入的parameters進行編碼,并添加到request中
/**
* 一般我們請求都會按key=value的方式帶上各種參數,GET方法參數直接加在URL上,POST方法放在body上,NSURLRequest沒有封裝好這個參數的解析,只能我們自己拼好字符串。AFNetworking提供了接口,讓參數可以是NSDictionary, NSArray, NSSet這些類型,再由內部解析成字符串后賦給NSURLRequest。
*/
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
如果 method 是 GET、HEAD、DELETE,那 parameter 將會被用來構建一個基于 url 編碼的查詢字符串(query url),并且這個字符串會直接加到request的url后面。對于 POST/PUT,它們會根據 parameterEncoding 屬性進行編碼,而后加到 request 的 http body 上。
4.用 request 創建 NSURLSessionDataTask
主要方法體如下:
__block NSURLSessionDataTask *dataTask = nil;
// 解決iOS8之前的一個bug
url_session_manager_create_task_safely(^{
// 用NSURLSession創建NSURLSessionDataTask
dataTask = [self.session dataTaskWithRequest:request];
});
...
用 NSURLSession 的 dataTaskWithRequest:
方法來創建 dataTask,這里需要注意的是方法被一個url_session_manager_create_task_safely
的 block 包起來,這里是為了解決 iOS 8 上的一個 bug。
5.給 dataTask 添加 Delegate
在這個方法里給 dataTask 添加了一個 AFURLSessionManagerTaskDelegate,并為 dataTask 提供進度管理功能,具體的是在[self setDelegate:delegate forTask:dataTask]
方法:
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 設置uploadProgress和downloadProgress這兩個NSProgress變量
[delegate setupProgressForTask:task];
// 給session task添加KVO
[self addNotificationObserverForTask:task];
[self.lock unlock];
這里首先把task.taskIdentifier
作為 key,delegate
作為 value 存在一個 MutableDictionary 里,然后設置上傳和下載兩個 progress,并給 task 添加上啟動和暫停的 KVO。
小結
通過這個流程可以初步了解 AFNetworking 內部方法調用關系,當然也可以看出其實 AFNetworking 就是對 NSURLSession 的高度地封裝。隨后的文章會結合源碼,來深入理解 AFNetworking 的實現原理。