在 《iOS 網(wǎng)絡——NSURLSession》中,我們介紹了 NSURLSession
的架構(gòu)及工作原理。本文蟹略,我們通過閱讀 AFNetworking 源代碼(版本號:2.6.3
)捧存,來介紹其設計架構(gòu)。
AFNetwoking 概述
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 的核心類。 -
AFHTTPSessionManager
:AFURLSessionManager
的子類户魏,主要用于 HTTP 請求驶臊。 -
AFURLRequestSerialization
:請求序列化器,用于將參數(shù)編碼為查詢字符串叼丑、HTTP 正文关翎,并根據(jù)需要設置合適的 HTTP 頭部字段。 -
AFURLResponseSerialization
:響應序列化器幢码,用于將數(shù)據(jù)解碼為對象笤休,還可以對傳入的響應和數(shù)據(jù)進行驗證。 -
AFSecurityPolicy
:通過安全連接評估服務器對固定的 X.509 證書和公鑰的信任症副。 -
AFNetworkReachabilityManager
:監(jiān)視網(wǎng)絡可達性店雅。
下面我們來介紹一下 AFNetworking 的核心——AFURLSessionManager
。
AFURLSessionManager
下圖所示為 AFURLSessionManager
的內(nèi)部結(jié)構(gòu)贞铣。下面闹啦,我們將以該結(jié)構(gòu)圖為指導,介紹 AFURLSessionManager
的工作原理辕坝。
持有屬性
我們首先瀏覽一下 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 的其他輔助類以及分類功能怠肋。
參考
(完)