本文來自簡書,原文位址:http://www.jianshu.com/p/4f1818568bc4
Write in the first【寫在最前】
AFNetWorking 基本是iOS開發中使用網絡通信架構的标配,這個架構本身比較龐大,也很複雜,但是使用起來非常非常簡單。
本篇文章主要從【AFN 内部邏輯處理過程】以Get請求為例,學習總結,
時間有限,知識并未全覆寫,有遺漏或者錯誤,忘指正。

AFNetworking
目錄:
- AFN GET内部邏輯處理
- AFN POST内部邏輯處理
- 總結
- 後續【AFN 架構 | 源碼 學習總結】
Business logic【AFN GET 内部邏輯處理】
這是 AFNetworking 發起一個 Get 請求的流程圖,大概可以分為這幾個步驟,下面會逐個解讀這個流程。
AFN-->GET業務邏輯處理.png
1. AFHTTPSessionManager 發起GET請求
manager-->GET請求
這個方法是 AFN 的 Get請求 的起點,其他 Get 請求的方法也都是直接或者間接調用這個方法來發起 Get 請求。這個方法的代碼量很少也很直覺,就是調用其他方法生成
NSURLSessionDataTask
對象的執行個體,然後調用
NSURLSessionDataTask
的
resume
方法發起請求。
2. 建立 NSURLSessionDataTask
建立-->NSURLSessionDataTask
這個方法是建立
NSURLSessionDataTask
對象執行個體并傳回這個執行個體。首先建立一個
NSMutableURLRequest
對象的執行個體,然後配置。之後是使用
NSMutableURLRequest
對象的執行個體建立
NSURLSessionDataTask
對象執行個體,然後配置,可以選擇性地傳入各類
Block
回調,用于監聽網絡請求的進度比如上傳進度,下載下傳進度,請求成功,請求失敗。
3. 配置 NSMutableURLRequest對象
配置-->NSMutableURLRequest對象
在這個方法中先使用了 url 建立了一個
NSMutableURLRequest
對象的執行個體,并且設定了
HTTPMethod
為
Get
方法(如果是Post方法,那麼這裡就是設定Post方法)然後使用KVC的方法設定了
NSMutableURLRequest
的一些屬性。
// 設定 NSMutableURLRequest 的屬性
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//allowsCellularAccess 允許使用資料流量
//cachePolicy 緩存政策
//HTTPShouldHandleCookies 處理Cookie
//HTTPShouldUsePipelining 批量請求
//networkServiceType 網絡狀态
//timeoutInterval 逾時
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}
配置-->NSMutableURLRequest對象
先設定 HTTP header,之後格式化請求參數,設定參數的編碼類型。這個是這個方法的基本操作流程。對于Get操作來說,參數是直接拼接在請求位址後面。
4. 配置 NSURLSessionDataTask對象
配置-->NSURLSessionDataTask對象
之後配置
NSMutableURLRequest
對象就需要配置
NSURLSessionDataTask
對象了。主要分為2個步驟,第一個步驟是建立
NSURLSessionDataTask
對象執行個體,第二個步驟是給
NSURLSessionDataTask
對象執行個體設定
Delegate
。用于實時了解網絡請求的過程。
給NSURLSessionDataTask對象執行個體設定Delegate.png
AFN 的代理統一使用
AFURLSessionManagerTaskDelegate
對象來管理,使用
AFURLSessionManagerTaskDelegate
對象來接管
NSURLSessionTask
網絡請求過程中的回調,然後再傳入
AFN
内部進行管理。
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate,
NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
如代碼所示
AFURLSessionManagerTaskDelegate
接管了
NSURLSessionTaskDelegate
,
NSURLSessionDataDelegate
,
NSURLSessionDownloadDelegate
的各種回調,然後做内部處理。這也是第三方網絡請求架構的重點,讓網絡請求更加易用,好用。
// 通過 task 的辨別符管理代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
// 将task和代理類綁定,task的taskIdentifier作為字典的key,delegate作為字典的value
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 給該task添加兩個KVO事件(Resume 和 Suspend)
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
通過
NSURLSessionTask
的
taskIdentifier
辨別符對
delegate
進行管理,隻要是用于識别該
NSURLSessionTask
的代理。
NSURLSessionTask 設定進度回調
設定各類回調 Block,給 NSURLSessionTask 使用 KVO 進行各種過程進度監聽。
#pragma mark -
// 給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];
}
監聽
NSURLSessionTask
被挂起 和 恢複的通知。
5. 網絡請求開始
// 發送GET請求
/**
GET: 請求路徑(不包含參數),url
parameters: 字典(發送給伺服器的資料~參數)
progress: 進度回調
success: 成功回調(task:請求任務、responseObject:響應體資訊JSON->OC對象)
failure: 失敗回調(error:錯誤資訊)
task.response: 響應頭
*/
- (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];
return dataTask;
}
當
NSURLSessionTask
建立和配置完畢之後,它并不會主動執行,而是需要我們主動調用
resume
方法,
NSURLSessionTask
才會開始執行。
6. 網絡請求回調
NSURLSessionDelegate 方法
NSURLSessionTaskDelegate 方法
AFN 裡面有關
NSURLSessionDelegate
的回調方法非常的多,這裡我們隻說和
NSURLSessionTask
相關的部分方法和
KVO
處理來進行說明,其他的大家可以參考源碼細看。
KVO監聽請求過程.png
收到響應資料.png
請求完成.png
對于我們的 Get請求 來說,我們最關注的莫過于關注請求過程進度,收到響應資料和請求完成這2個回調。
KVO監聽的屬性值發生變化:
// KVO監聽的屬性值發生變化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
NSLog(@"countOfBytesReceived");
// 這個是在Get請求下,網絡響應過程中已經收到的資料量
// 已經收到
self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
NSLog(@"countOfBytesExpectedToReceive");
// 這個是在Get請求下,網絡響應過程中期待收到的資料量
// 期待收到
self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
NSLog(@"countOfBytesSent");
// 已經發送
self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
NSLog(@"countOfBytesExpectedToSend");
// 期待發送
self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] 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);
}
}
}
收到請求響應:
// 收到請求響應
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSLog(@"收到請求響應");
// 允許處理伺服器的響應,才會繼續接收伺服器傳回的資料
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
// 是否有收到請求響應的回調Block
if (self.dataTaskDidReceiveResponse) {
// 若有調用該Block
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
// 是否有請求響應完成的回調Block
if (completionHandler) {
// 若有調用該Block
completionHandler(disposition);
}
}
請求完成:
// 請求完成
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
NSLog(@"請求完成");
// 取出該NSURLSessionTask的代理對象
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];
// NSURLSessionTask任務完成之後,移除該NSURLSessionTask的代理對象
[self removeDelegateForTask:task];
}
// 是否有請求完成的回調Block
if (self.taskDidComplete) {
// 若有調用改Block
self.taskDidComplete(session, task, error);
}
}
因為在配置 NSURLSessionDataTask 對象的時候我們有給 NSURLSessionTask 做了一系列配置,那麼當 NSURLSessionDataTask 任務完成之後,我們需要将該 NSURLSessionDataTask 的一系列配置全部清理掉。
這個是我們的配置過程:
// 通過task的辨別符管理代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
那麼對應的清理過程是這樣的,就是設定過程中做了什麼,在清理過程中就需要去掉什麼。
// 給task移除delegate
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[self.lock lock];
[delegate cleanUpProgressForTask:task];
[self removeNotificationObserverForTask:task];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
cleanUpProgressForTask.png
removeNotificationObserverForTask.png
Business logic【AFN POST内部邏輯處理】
請求序列化方法
#pragma mark - AFURLRequestSerialization
// 設定Header和請求參數
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
// 判斷header的field是否存在,如果不存在則設定,存在則跳過
if (![request valueForHTTPHeaderField:field]) {
// 設定 header
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
// 用傳進來的自定義block格式化請求參數
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
// 預設的格式化方式
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// 判斷是否是GET/HEAD/DELETE方法, 對于GET/HEAD/DELETE方法,把參數加到URL後面
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
// 判斷是否有參數
if (query && query.length > ) {
// 拼接請求參數
NSLog(@"query-->%@",query);
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上,大多是POST PUT
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
// 設定Content-Type HTTP頭,告訴服務端body的參數編碼類型
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
如果是 Post 請求,那麼請求參數是沒有拼接在 URL 上面,而是放在
body
上,這是 Post 和 Get 請求的最大差別了,其他過程和Get 請求并沒有太多差別。
總結
AFN發起Get請求主要分為以下步驟:
- 1.建立
NSURLSessionDataTask
- 2.配置
NSURLSessionDataTask
- 3.設定
NSURLSessionDataTask的Delegate
- 4.調用
的NSURLSessionDataTask
方法開始請求resume
- 5.在
的方法裡面處理網絡請求的各個過程Delegate
- 6.清理
的配置NSURLSessionDataTask
其實也就是使用
NSURLSessionDataTask
的步驟,AFN在這幾個步驟加了一些封裝,讓我們的使用更簡單。
【轉載原著】iOS網絡架構-AFNetworking3.1.0源碼解讀
相關「所有學習總結都在這裡了」
- AFN 【架構 | 源碼 學習總結】
期待
- 如果在閱讀過程中遇到 error,希望你能 Issues 我,謝謝。
- 如果你想為【本文相關】分享點什麼,也希望你能 Issues 我,我非常想為這篇文章增加更多實用的内容,謝謝。
- 「部落格原文」,對本文我會【不定時、持續更新、一些 學習心得與文章、實用才是硬道理】^_^.