iOS 網(wǎng)絡(2)——AFNetworking

【原文鏈接】

《iOS 網(wǎng)絡——NSURLSession》中,我們介紹了 NSURLSession 的架構(gòu)及工作原理。本文蟹略,我們通過閱讀 AFNetworking 源代碼(版本號:2.6.3)捧存,來介紹其設計架構(gòu)。

AFNetwoking 概述

image

AFNetworking 是一套適用于 iOS瑰排、macOS、watchOS暖侨、tvOS 的網(wǎng)絡庫椭住。AFNetworking 2.0 之后的版本構(gòu)建在基于 NSURLSession 的 Foundation URL 加載系統(tǒng)之上。AFNetworking 擴展了 Cocoa 內(nèi)置的強大的高級網(wǎng)絡抽象字逗,采用模塊化設計京郑,功能豐富宅广,是最廣泛使用的開源項目之一。

AFNetworking 架構(gòu)

AFNetworking 框架主要包含兩部分:

  • AFNetworking 核心功能
  • UIKit+AFNetworking 分類功能

這里我們主要介紹 AFNetworking 的核心功能些举,其整體的架構(gòu)如下圖所示跟狱。AFNetworking 主要包含了 6 個類:

  • AFURLSessionManager:AFNetworking 的核心類。
  • AFHTTPSessionManagerAFURLSessionManager 的子類户魏,主要用于 HTTP 請求驶臊。
  • AFURLRequestSerialization:請求序列化器,用于將參數(shù)編碼為查詢字符串叼丑、HTTP 正文关翎,并根據(jù)需要設置合適的 HTTP 頭部字段。
  • AFURLResponseSerialization:響應序列化器幢码,用于將數(shù)據(jù)解碼為對象笤休,還可以對傳入的響應和數(shù)據(jù)進行驗證。
  • AFSecurityPolicy:通過安全連接評估服務器對固定的 X.509 證書和公鑰的信任症副。
  • AFNetworkReachabilityManager:監(jiān)視網(wǎng)絡可達性店雅。
image

下面我們來介紹一下 AFNetworking 的核心——AFURLSessionManager

AFURLSessionManager

下圖所示為 AFURLSessionManager 的內(nèi)部結(jié)構(gòu)贞铣。下面闹啦,我們將以該結(jié)構(gòu)圖為指導,介紹 AFURLSessionManager 的工作原理辕坝。

image

持有屬性

我們首先瀏覽一下 AFURLSessionManager 所持有的屬性窍奋。

/// 外部公開的屬性
@interface AFURLSessionManager
/// 管理的 Session
@property (readonly, nonatomic, strong) NSURLSession *session;
/// 代理回調(diào)執(zhí)行的隊列
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
/// 使用 dataTaskWithRequest:success:failure: 創(chuàng)建,使用 GET 或 POST 執(zhí)行的 data task 的響應酱畅。默認值是 AFJSONResponseSerializer
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
/// Session 使用的安全策略琳袄,用于評估服務器對安全連接的信任程度。AFURLSessionManager 使用默認策略 defaultPolicy
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
/// 網(wǎng)絡可達性管理器纺酸。AFURLSessionManager 默認使用 sharedManager
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;

/// ---------------------

/// 被管理 session 當前運行的所有類型的任務窖逗,如:data,upload餐蔬,download 等
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
/// 被管理 session 當前運行的 data 任務
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
/// 被管理 session 當前運行的 upload 任務
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
/// 被管理 session 當前運行的 download 任務
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
/// completionBlock 執(zhí)行所在的隊列碎紊。如果是 NULL,則在主線程執(zhí)行
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
/// completionBlock 執(zhí)行所在的 group樊诺。如果是 NULL仗考,則在一個私有的 group 中執(zhí)行
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;

@end
/// 內(nèi)部私有的屬性
@interface AFURLSessionManager ()
@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
@property (readwrite, nonatomic, strong) NSLock *lock;

/// ---------------------

@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics;
#endif
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
@end

初始化

首先,AFURLSessionManager 對象持有一個 NSURLSessionConfiguration 對象(會話配置對象)词爬。使用會話配置對象即可初始化并持有一個 NSURLSession 對象(會話對象)秃嗜。會話對象的初始化過程如下代碼所示:

- (NSURLSession *)session {
    @synchronized (self) {
        if (!_session) {
            _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
        }
    }
    return _session;
}

注意,初始化會話對象后,會話對象將 AFURLSessionManager 設置成自己的代理痪寻。因此螺句,AFURLSessionManager 需要實現(xiàn)相關(guān)協(xié)議所聲明的那些方法。這些協(xié)議有:

  • NSURLSessionDelegate
  • NSURLSessionTaskDelegate
  • NSURLSessionDataDelegate
  • NSURLSessionDownloadDelegate

為了能夠讓開發(fā)者實現(xiàn)自定義處理會話級事件和任務級事件橡类,AFURLSessionManager 提供了大量設置回調(diào)的方法,基本可以覆蓋到協(xié)議所聲明的每一個方法芽唇。這些回調(diào)被 AFURLSessionManager 在內(nèi)部持有顾画,只有在對應的協(xié)議方法被調(diào)用時,回調(diào)方法才會被執(zhí)行匆笤。

進度管理

AFURLSessionManager 作為會話對象的代理研侣,基本可以處理所有任務對象。但是炮捧,對于上傳任務庶诡、下載任務進行進度管理,使用 AFURLSessionManager 來管理是較為繁瑣的咆课,也是不合理的末誓。主要有以下幾點原因:

  • 在上傳任務和下載任務的整個生命周期中都需要持有一個進度對象 NSProgress
  • 在下載任務的整個生命周期中還需要持有一個接收數(shù)據(jù)對象书蚪,用于持續(xù)接收數(shù)據(jù)喇澡。
  • AFURLSessionManager 主要用于管理會話,根據(jù)職責單一原則殊校,它不應該管理任務對象的具體細節(jié)晴玖。

因此,AFNetworking 提供了 AFURLSessionManagerTaskDelegate 這樣一個類來管理特定任務的進度为流、數(shù)據(jù)呕屎。

AFURLSessionManager 內(nèi)部維護了一個可變字典屬性 mutableTaskDelegatesKeyedByTaskIdentifier,以任務對象的標識符 taskIdentifier 作為鍵敬察,以 AFURLSessionManagerTaskDelegate 對象作為值秀睛。在初始化任務對象時,就綁定一個 AFURLSessionManagerTaskDelegate 對象静汤,以處理進度琅催、下載數(shù)據(jù)。具體代碼如下所示:

// 根據(jù)特定 request 創(chuàng)建一個 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 
{
    // 創(chuàng)建任務對象
    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    // 為任務對象設置代理對象虫给、上傳進度回調(diào)藤抡、下載進度回調(diào)、完成回調(diào)
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

- (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
{
    // 創(chuàng)建一個 AFURLSessionManagerTaskDelegate 對象抹估,用以處理進度缠黍、下載數(shù)據(jù)
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    // 用來標識任務對象所屬的 SessionManager
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    // 保存 NSURLSessionTask <-> AFURLSessionManagerTaskDelegate 的映射關(guān)系
    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

AFURLSessionManagerTaskDelegate 主要實現(xiàn)了以下這些協(xié)議方法。在這些方法內(nèi)實現(xiàn)進度管理药蜻、下載管理瓷式。AFURLSessionManager 雖然也實現(xiàn)了這些協(xié)議替饿,但是最終還是會調(diào)用 AFURLSessionManagerTaskDelegate 中的實現(xiàn)。

  • NSURLSessionTaskDelegate
    • URLSession:task:didCompleteWithError:
    • URLSession:task:didFinishCollectingMetrics:
  • NSURLSessionDataDelegate
    • URLSession:dataTask:didReceiveData:
    • URLSession:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:

通知轉(zhuǎn)發(fā)

NSURLSession 定義了一系列通知贸典,為了提供更完美的封裝视卢,AFURLSessionManager 捕獲了這些通知,將它們轉(zhuǎn)換成自己所定義的通知后廊驼,再進行轉(zhuǎn)發(fā)据过。

這里,就用到了任務對象的 taskDescription 屬性妒挎。taskDescription 屬性描述了該任務對象所屬的 Session Manager绳锅。因為應用程序中可能會初始化多個 AFURLSessionManager。Session Manager 只有在捕獲到它所管理的任務對象發(fā)出的通知后才會進行封裝和轉(zhuǎn)發(fā)酝掩。如:

  • AFNetworkingTaskDidResumeNotification
  • AFNetworkingTaskDidSuspendNotification

AFHTTPSessionManager

接下來鳞芙,我們再來介紹一下 AFURLSessionManager 的子類——AFHTTPSessionManager

首先期虾,我們來看一下 AFHTTPSessionManager 所特有的屬性原朝。

@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>

@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

@end

由于,AFHTTPSessionManager 主要用于 HTTP 請求彻消,所以它為 HTTP 請求提供了一個請求序列化器和一個響應序列化器竿拆,并分別設置默認值為 HTTP 和 JSON。

除此之外宾尚,AFHTTPSessionManager 還封裝了 AFURLSessionManager 的一些復雜的任務對象初始化方法丙笋,并提供了一些非常簡單的便利方法,用以提供各種 HTTP 請求煌贴。如下所示:

- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                            parameters:(nullable id)parameters
                               headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                              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
                                headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                success:(nullable void (^)(NSURLSessionDataTask *task))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
                                
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                                headers:(nullable NSDictionary <NSString *, NSString *> *)headers
              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
                               headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                               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
                                 headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                 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
                                  headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                  success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                  failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

結(jié)論

總而言之御板,AFNetworking 實際上只是對 NSURLSession 高度地封裝, 提供一些簡單易用的 API 方便我們在 iOS 開發(fā)中發(fā)出網(wǎng)絡請求并在其上更快地構(gòu)建網(wǎng)絡層組件并提供合理的接口。

后面牛郑,有機會我們繼續(xù)深入了解一下 AFNetworking 的其他輔助類以及分類功能怠肋。

參考

  1. AFNetworking
  2. AFNetworking 概述(一)

(完)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淹朋,隨后出現(xiàn)的幾起案子笙各,更是在濱河造成了極大的恐慌,老刑警劉巖础芍,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杈抢,死亡現(xiàn)場離奇詭異,居然都是意外死亡仑性,警方通過查閱死者的電腦和手機惶楼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人歼捐,你說我怎么就攤上這事何陆。” “怎么了豹储?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵贷盲,是天一觀的道長。 經(jīng)常有香客問我颂翼,道長晃洒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任朦乏,我火速辦了婚禮,結(jié)果婚禮上氧骤,老公的妹妹穿的比我還像新娘呻疹。我一直安慰自己,他們只是感情好筹陵,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布刽锤。 她就那樣靜靜地躺著,像睡著了一般朦佩。 火紅的嫁衣襯著肌膚如雪并思。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天语稠,我揣著相機與錄音宋彼,去河邊找鬼。 笑死仙畦,一個胖子當著我的面吹牛输涕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慨畸,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼莱坎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了寸士?” 一聲冷哼從身側(cè)響起檐什,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弱卡,沒想到半個月后乃正,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡谐宙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年烫葬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡搭综,死狀恐怖垢箕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兑巾,我是刑警寧澤条获,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蒋歌,受9級特大地震影響帅掘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜堂油,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一修档、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧府框,春花似錦吱窝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至系宜,卻和暖如春照激,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盹牧。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工俩垃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欢策。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓吆寨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親踩寇。 傳聞我的和親對象是個殘疾皇子啄清,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容