AFNetworking是iOS平臺(實際上它也支持OS X,watch OS)里做網絡框架最出名的那個http網絡層框架。接口封裝良好,支持各種標準的http請求,更新及時,github上2W+的star,幾乎都要成iOS的業界標準了。一個http請求發出去,拿到一個字典對象(如果是json序列化),然后取出來在成功回調里各種刷UI或者該干嘛干嘛,多開心啊_,你問我滋磁不滋磁,我當然是滋磁滴。本系列將基于以下模塊(按AF的文件夾分的)在做AF的源碼解析,話不多說直接上正題:
AFNetworking里分為以下幾部分:
- NSURLSession。核心代碼,涉及客戶端發起網絡請求到收到響應數據的全過程處理。請求的數據處理以及響應的數據處理在放在序列化那塊。
- Reachability。檢查網絡狀況
- Security。設計網絡安全部分,主要用于https中防中間人攻擊
- Serialization。序列化,核心代碼。主要涉及到構造網絡請求以及對拿到的網絡請求數據做一些處理。
- UIKit。主要是對一些常用UI控件做網絡交互方便的擴展。
NSURLSession模塊主要包括以下兩個類:
AFHttpSessionManager和AFURLSessionManager。AFHttpSessionManager繼承自AFURLSessionManager,是主要http網絡請求的接口暴露層。封裝了get,post,put,delete,head,patch等標準http請求。在將具體討論AF源碼之前,先說下iOS7.0以后推出的應用層網絡請求新類,NSURLSession && NSURLSessionTask及相關NSURLSessionTask子類和代理類。
NSURLSession 可以理解為一個連接池管理對象,而不是一個鏈接類。默認情況下,OS X 和iOS 中NSURLSession最大的鏈接數分別是6和4(詳情見NSURLSessionConfiguration的HTTPMaximumConnectionsPerHost屬性)。NSURLSession相比原來的NSURLConnection提供了一些新的好處是支持http2.0,而且提供了<font color=red> 鏈接復用 </font>,而且還提供了后臺下載功能,也是就即使app退到后臺,依然可以讓下載任務不停。鏈接復用對于http/https請求性能提升是有很大好處的,不需要每次都建立鏈接的三次握手以及https的身份驗證過程,具體性能提升原理和過程可以參考 IP,TCP 和 HTTP 和 別說你會AFNetworking3.0/NSURLSession 這兩篇文章。順便說一下直接使用原生的AFNetworking 3.0接口是不支持鏈接復用的,后見面解析源碼的時候會發現AF每次發一個網絡請求都會創建一個帶默認配置的NSURLSession。
NSURLSessionTask是個抽象類,可以理解為任務就是一個請求。由于網絡請求類型的不同,有的是發送一個get請求,拿到一小塊數據,有的是上傳一個比較大的文件,有的是要下載一個比較大的數據,由此而引申出來NSURLSessionTask的對應三個子類:NSURLSessionDataTask, NSURLSessionUploadTask, NSURLSessionDownloadTask和各自對應的相關代理類。大致知道這幾個類的區別和使用場景就可以了,這些類具體詳細區別了就不展開了。
AFHttpSessionManager 暴露接口
AFHttpSessionManger是開發者接觸最多也是直接使用的類,它的功能是直接暴露出各種標準http方法共開發者調用。方法名字都是[url:parameters:successBlock:failedBlock]這種比較統一風格的,簡單易用。它還有一些屬性和構造方法:
/**
The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods.
*/
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
/**
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
@warning `requestSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
/**
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
///---------------------
/// @name Initialization
///---------------------
/**
Creates and returns an `AFHTTPSessionManager` object.
*/
+ (instancetype)manager;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
@param url The base URL for the HTTP client.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
This is the designated initializer.
@param url The base URL for the HTTP client.
@param configuration The configuration used to create the managed session.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url
sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
baseURL類似host一樣的東西,一旦設置好后,所有的網絡請求都只寫接口名字而不用寫host,建議把這個baseURL作為一個宏或者配置文件,這樣當整個app的后臺服務器版本升級(一般會不會改域名,只是改version字段),只需要改一處就可以了。
manger方法是個類方法,看著名字很像單例是吧
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
為了可自己定制session,AFHttpSessionManger提供了initWithBaseURL:sessionConfiguration方法,但是這個方法里面又是這樣滴:
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
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;
}
而在super方法里也就是AFURLSessionManager的initWithSessionConfiguration 方法里是每次都會新建一個session。也就是一個AFHttpSessionManager實例對應一個NSURLSession實例,雖然NSURLSession本身提供了鏈接復用,但是上層的“不當使用”關閉了它的功能。
其實也不是不當使用,在AFHttpSessionManager類的注釋文件里,作者做了如下說明:
Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass AFHTTPSessionManager
, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application.
For developers targeting iOS 6 or Mac OS X 10.8 or earlier, AFHTTPRequestOperationManager
may be used to similar effect.
看到這句話其實作者希望我們自己去封裝AFNetworking的,并使用單例的方式來調用。
AFHttpSessionManager的構造請求方法(上傳文件除外,上傳文件的body構造與一般的請求不一樣,會用到輸入流寫到HTTPBodyStream中,而不是HTTPbody)最終都會調用
- (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
這個方法會構造好請求(具體的請求構造是在AFURLSessionManager 里完成的),生成一個NSURLSessionDataTask對象,并根據設置好相應的block回調。拿到這個task后接口方法調用task對象的resume的方法來發起網路請求。可以看到接口層比較簡單,也應該比較簡單。當然邏輯守恒定律,總有一些地方要復雜的。AFURLSessionManager 里面做了相對而言復雜的工作,包括構造各種請求(主要是各種填充頭部和httpbody),然后實現各種不同task的代理方法來完成數據的組裝等。
AFURLSessionManager 構造請求 && 處理數據回調
AFURLSessionManager 負責具體構造請求,并實現了NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate 這四個代理。用于處理收到網絡響應時的回調處理。
先來看下AFURLSessionManager 都有哪些屬性
@property (readonly, nonatomic, strong) NSURLSession *session;
/**
The operation queue on which delegate callbacks are run.
*/
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
/**
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
///-------------------------------
/// @name Managing Security Policy
///-------------------------------
/**
The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified.
*/
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
#if !TARGET_OS_WATCH
///--------------------------------------
/// @name Monitoring Network Reachability
///--------------------------------------
/**
The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default.
*/
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
#endif
///----------------------------
/// @name Getting Session Tasks
///----------------------------
/**
The data, upload, and download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
/**
The data tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
/**
The upload tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
/**
The download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
///-------------------------------
/// @name Managing Callback Queues
///-------------------------------
/**
The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
/**
The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
*/
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
///---------------------------------
/// @name Working Around System Bugs
///---------------------------------
/**
Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default.
@bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again.
@see https://github.com/AFNetworking/AFNetworking/issues/1675
*/
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
///---------------------
/// @name Initialization
///---------------------
/**
Creates and returns a manager for a session created with the specified configuration. This is the designated initializer.
@param configuration The configuration used to create the managed session.
@return A manager for a newly-created session.
*/
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
/**
Invalidates the managed session, optionally canceling pending tasks.
@param cancelPendingTasks Whether or not to cancel pending tasks.
*/
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
session:創建的NSURLSession對象。由于NSURLSession本身的構造接口提供了一個回調隊列,因此多了一個NSOperationQueue,如果不設置,NSURLSession會自動創建一個串行隊列,所有的回調都將在這個隊列里進行。
tasks:當前正在執行的全部task數組
dataTasks:當前正在執行datatask數組
uploadTasks:當前正在執行上傳task數組
downloadTasks:當前正在執行的下載task數組
這些task在執行完了之后會被移除數組并銷毀。
securityPolicy:在https鏈接當需要做身份驗證時采取的安全策略。默認是不會進行證書驗證。
接下來就是暴露給外面的構造請求的方法了,然后就是設置NSURLSession各種狀態下的處理block。這個占據了太多篇幅,代理方法的名字就能很好表達方法的意思了,所以就不具體分析了。
需要注意的是處理數據的過程中,對于上傳進度,下載進度是通過觀察NSURLSessionTask的這幾個屬性
然后根據taskid取到task在執行相應的上傳和下載進度回調。
接口層的分析基本就這些,下篇將具體分析在構造請求的過程中,是怎么構造的,對于文件上傳的post請求又是怎么處理的以及對響應數據又是怎么處理的。