天天看點

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

本文來自簡書,原文位址:http://www.jianshu.com/p/4f1818568bc4

Write in the first【寫在最前】

AFNetWorking 基本是iOS開發中使用網絡通信架構的标配,這個架構本身比較龐大,也很複雜,但是使用起來非常非常簡單。

本篇文章主要從【AFN 内部邏輯處理過程】以Get請求為例,學習總結,

時間有限,知識并未全覆寫,有遺漏或者錯誤,忘指正。

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

 AFNetworking

目錄:
  1. AFN GET内部邏輯處理
  2. AFN POST内部邏輯處理
  3. 總結
  4. 後續【AFN 架構 | 源碼 學習總結】

Business logic【AFN GET 内部邏輯處理】

這是 AFNetworking 發起一個 Get 請求的流程圖,大概可以分為這幾個步驟,下面會逐個解讀這個流程。

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

AFN-->GET業務邏輯處理.png

1. AFHTTPSessionManager 發起GET請求

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

manager-->GET請求

這個方法是 AFN 的 Get請求 的起點,其他 Get 請求的方法也都是直接或者間接調用這個方法來發起 Get 請求。這個方法的代碼量很少也很直覺,就是調用其他方法生成 

NSURLSessionDataTask

對象的執行個體,然後調用 

NSURLSessionDataTask

 的 

resume

 方法發起請求。

2. 建立 NSURLSessionDataTask

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

建立-->NSURLSessionDataTask

這個方法是建立 

NSURLSessionDataTask

 對象執行個體并傳回這個執行個體。首先建立一個 

NSMutableURLRequest

 對象的執行個體,然後配置。之後是使用 

NSMutableURLRequest

 對象的執行個體建立

NSURLSessionDataTask

 對象執行個體,然後配置,可以選擇性地傳入各類

Block

回調,用于監聽網絡請求的進度比如上傳進度,下載下傳進度,請求成功,請求失敗。

3. 配置 NSMutableURLRequest對象

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

配置-->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;
}
           
AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

配置-->NSMutableURLRequest對象

先設定 HTTP header,之後格式化請求參數,設定參數的編碼類型。這個是這個方法的基本操作流程。對于Get操作來說,參數是直接拼接在請求位址後面。

4. 配置 NSURLSessionDataTask對象

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

配置-->NSURLSessionDataTask對象

之後配置 

NSMutableURLRequest

 對象就需要配置 

NSURLSessionDataTask

 對象了。主要分為2個步驟,第一個步驟是建立 

NSURLSessionDataTask

 對象執行個體,第二個步驟是給

NSURLSessionDataTask

 對象執行個體設定 

Delegate

。用于實時了解網絡請求的過程。

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

給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

的代理。

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

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. 網絡請求回調

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

NSURLSessionDelegate 方法

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

NSURLSessionTaskDelegate 方法

AFN 裡面有關 

NSURLSessionDelegate

 的回調方法非常的多,這裡我們隻說和 

NSURLSessionTask

 相關的部分方法和 

KVO

 處理來進行說明,其他的大家可以參考源碼細看。

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

KVO監聽請求過程.png

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

收到響應資料.png

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

請求完成.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];
}
           
AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

cleanUpProgressForTask.png

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

removeNotificationObserverForTask.png

Business logic【AFN POST内部邏輯處理】

AFNetworking(v3.0+)架構學習總結(二内部邏輯處理過程)

請求序列化方法

#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 我,我非常想為這篇文章增加更多實用的内容,謝謝。
  • 「部落格原文」,對本文我會【不定時、持續更新、一些 學習心得與文章、實用才是硬道理】^_^.

繼續閱讀