AFNetworking詳解(1)

AFNetworking 是一個 iOS 平臺的網絡框架,簡潔易懂,因此在實際項目開發中用的極多,AFNetworking 本質上仍是基於蘋果自身的網絡通訊機制,這裏就剖析一下其內部代碼。xcode

框架概述

將 AFNetworking 代碼下載過來,目錄樹以下:網絡

total 240
drwxr-xr-x@ 15 chasontang  staff   510B  4  1 01:28 AFNetworking
-rw-r--r--@  1 chasontang  staff   2.5K  4  1 01:28 AFNetworking.podspec
drwxr-xr-x@  5 chasontang  staff   170B  4 27 17:03 AFNetworking.xcodeproj
drwxr-xr-x   4 chasontang  staff   136B  4 27 17:03 AFNetworking.xcworkspace
-rw-r--r--@  1 chasontang  staff    87K  4  1 01:28 CHANGELOG.md
-rw-r--r--@  1 chasontang  staff   4.1K  4  1 01:28 CONTRIBUTING.md
drwxr-xr-x@ 19 chasontang  staff   646B  4 27 17:04 Example
drwxr-xr-x@  5 chasontang  staff   170B  4  1 01:28 Framework
-rw-r--r--@  1 chasontang  staff   1.1K  4  1 01:28 LICENSE
-rw-r--r--@  1 chasontang  staff    14K  4  1 01:28 README.md
drwxr-xr-x@  7 chasontang  staff   238B  4 27 17:10 Tests
drwxr-xr-x@ 22 chasontang  staff   748B  4  1 01:28 UIKit+AFNetworking
drwxr-xr-x@ 17 chasontang  staff   578B  4  1 01:28 fastlane

可是本質上只有 AFNetworking 文件夾是實際的代碼,而其餘都是樣例工程、測試用例、使用工具類等。AFNetworking 文件夾下只有 6 個類文件和 1 個彙總頭文件,總共大約 5000 行代碼。session

AFNetworking.h - header file
AFNetworkReachabilityManager - class
AFSecurityPolicy - class
AFURLRequestSerialization - class
AFURLResponseSerialization - class
AFURLSessionManager - class
AFHTTPSessionManager - class

不過須要注意的是,看起來只存 6 個類文件,可是實際上一個類文件中包含了多個存在繼承關係的類,這點確實是瑕疵,容易形成誤解。
蘋果自身提供的網絡通訊框架也被稱之爲 URL loading system,主要使用 NSURLSessionNSURLConnection 兩個類做爲通訊管理類,其中,NSURLSession 只能進行異步通訊,並且能夠進行後臺網絡通訊,只能用於 iOS 7 及之後版本。而 NSURLConnection 既能夠異步也能夠同步通訊。AFNetworking 則是基於這套機制封裝的,而且是繼承 NSURLSession 類。
注意:本文所指 AFNetworking 是指 3.x 版本,NSURLConnectionOperation 支持已經被移除了,框架要求最低爲 iOS 7 版本。
框架類繼承以下:框架

  • NSURLSession異步

    • AFURLSessionManagerasync

    • AFHTTPSessionManager函數

  • AFURLRequestSerialization工具

    • AFHTTPRequestSerializer測試

    • AFJSONRequestSerializerurl

    • AFPropertyListRequestSerializer

  • AFURLResponseSerialization

    • AFHTTPResponseSerializer

    • AFJSONResponseSerializer

    • AFXMLParserResponseSerializer

    • AFPropertyListResponseSerializer

    • AFImageResponseSerializer

    • AFCompoundResponseSerializer

  • AFSecurityPolicy

  • AFNetworkReachabilityManager

源代碼剖析

AFNetworking.h

#import <Foundation/Foundation.h>
#import <Availability.h>
#import <TargetConditionals.h>

#ifndef _AFNETWORKING_
    #define _AFNETWORKING_

    #import "AFURLRequestSerialization.h"
    #import "AFURLResponseSerialization.h"
    #import "AFSecurityPolicy.h"

#if !TARGET_OS_WATCH
    #import "AFNetworkReachabilityManager.h"
#endif

    #import "AFURLSessionManager.h"
    #import "AFHTTPSessionManager.h"

#endif /* _AFNETWORKING_ */

很常規的思路,導入所需頭文件,宏定義用於防止頭文件重複包含,蘋果手錶平臺不導入 AFNetworkReachabilityManager 頭文件。

AFHTTPSessionManager

首先這個類是繼承自 AFURLSessionManager,實現了 <NSSecureCoding, NSCopying> 協議,而 AFURLSessionManager 則是繼承自 NSObject,而且實現了 <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
對於大多數開發來講,都是使用 AFHTTPSessionManager 做爲網絡請求中心。其中有兩個重要成員變量:requestSerializer、responseSerializer 分別是請求序列化器和迴應序列化器,應該不用多說,從字面上也能猜出其內容。而後就分別是如下方法:

+ (instancetype)manager;
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                            parameters:(nullable id)parameters
                              progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(nullable id)parameters
     constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                       success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
              constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                   parameters:(nullable id)parameters
                      success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                     parameters:(nullable id)parameters
                        success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                        failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                      parameters:(nullable id)parameters
                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                         failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

一個工廠方法,而後就是 GET、HEAD、POST、PUT、PATCH 和 DELETE 方法。標記爲 DEPRECATED_ATTRIBUTE 的則是棄用方法,一些方法帶有 progressBlock,便於計算加載過程。工廠方法 + (instancetype)manager 實際上仍是建立了一個 baseURL 爲 nil 的實例,因此應當避免調用工廠方法,而是手動建立對象。而全部初始化方法都會調用 initWithBaseURL:sessionConfiguration: 方法,如下是初始化方法代碼:

self = [super initWithSessionConfiguration:configuration];
if (!self) {
    return nil;
}

// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
    url = [url URLByAppendingPathComponent:@""];
}

self.baseURL = url;

self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];

return self;

主要就是整理了 baseURL,而後建立了 AFHTTPRequestSerializer 做爲請求序列化器,AFJSONResponseSerializer 做爲相應序列化器,也就是默認狀況下返回 JSON。
再來看請求方法,很容易就能看出其流程爲:變體請求方法 -> 基請求方法 -> dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:,就是返回了類型爲 NSURLSessionDataTask 的對象,而後調用 [dataTask resume] 執行請求。

- (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
{
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }

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

注意,除了一個 POST 方法的變體方法之外,這是全部 HTTP 請求方法的公共出口,主要就是建立一個 NSURLSessionDataTask 對象。POST 方法有一個變體方法是使用 FORMDATA 的形式發起 HTTP 請求,因此沒法概括到公共抽象代碼內,所以獨立實現了其方法,可是實際上也是差很少的,這裏就很少講了。
首先就是建立 NSMutableURLRequest 對象,若是出錯,則使用 GCD 調用 failure 回調函數。不然其調用父類的 dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: 方法建立 NSURLSessionDataTask 對象。

AFNetworkReachabilityManager

這個類用於檢查網絡聯網狀態,其實是模仿蘋果官方示例代碼 reachability,這裏就不提了,各位自行觀看代碼實現。

相關文章
相關標籤/搜索