AFNetworking
一、簡介
AFNetworking 是對iOS中網(wǎng)絡(luò)請求方式 NSURLSession 的封裝,之前有NSURLConnection,后面已經(jīng)被廢棄。
在分析AFNetworking之前先來介紹一下使用 NSURLSession 發(fā)起一次網(wǎng)絡(luò)請求的步驟:
- 創(chuàng)建NSURLSessionCoftig對象
- 創(chuàng)建NSURLSession對象
- 創(chuàng)建task
- 調(diào)用resume開始執(zhí)行請求
- 代理響應(yīng)網(wǎng)絡(luò)事件及數(shù)據(jù)
二、框架結(jié)構(gòu)
這里分析4.0版本的結(jié)構(gòu)。主要分為五個部分:
1、NSURLSession:網(wǎng)絡(luò)通信模塊,兩個文件AFURLSessionManager,AFHTTPSessionManager。AFHTTPSessionManager繼承與AFURLSessionManager
2、Reachability 網(wǎng)絡(luò)狀態(tài)監(jiān)聽,AFNetworkReachabilityManager
3、Security 網(wǎng)絡(luò)通訊安全策略模塊,AFSecurityPolicy
4、Serialization 序列化,AFURLResponseSerialization,AFURLRequestSerialization
5、UIKit 對于UIKit的擴展庫
三、AFHTTPSessionManager 對象初始化過程,中間做了哪些事情
首先來看初始化AFHTTPSessionManager對象的過程,其中需要注意的是[AFHTTPSessionManager manager]方法并不是單例,調(diào)用的也是下面的方法返回。在AFHTTPSessionManager中主要做了 1、調(diào)用父類初始化方法,2、給URL加'/', 3、給requestSerializer、responseSerializer設(shè)置默認值
給url加'/'的目的應(yīng)該是1、防重發(fā) 2、方便在url后面拼接參數(shù)。如get請求方式
- (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;
}
下面來看AFURLSessionManager初始化方法。這里就不貼代碼了,需要的自己打開AF4.0源碼看吧。主要步驟有:
- sessionConfiguration 屬性持有 configuration ,若傳入configuration為空,設(shè)置默認defaultSessionConfiguration。包含緩存策略,ID,超時時間 ...
- 初始化代理方法執(zhí)行的隊列operationQueue,并設(shè)置 maxConcurrentOperationCount 值為1。即串行執(zhí)行
- responseSerializer 解析方式,
- securityPolicy 使用defaultPolicy初始化,https證書校驗對象,用來校驗服務(wù)端安全信任鏈接
- reachabilityManager 網(wǎng)絡(luò)狀態(tài)監(jiān)聽。
- mutableTaskDelegatesKeyedByTaskIdentifier 類型是NSMutableDictionary,key是NSURLSessionTask的唯一NSUInteger類型標(biāo)識,value是對應(yīng)的AFURLSessionManagerTaskDelgate對象
- lock ,NSLock類型鎖創(chuàng)建,為了保證線程安全
- session對象創(chuàng)建,3.0版本是直接創(chuàng)建。4.0版本是懶加載形式創(chuàng)建,并使用@synchronized互斥鎖保證只創(chuàng)建一次
- session 調(diào)用getTasksWithCompletionHandler方法獲取 之前的task,創(chuàng)建AFURLSessionManagerTaskDelegate。綁定task,并加入mutableTaskDelegatesKeyedByTaskIdentifier字典中
manager的研究先到這里,里面各種配置的具體實現(xiàn),暫時先不往深處介紹了,下面來看下request的實現(xiàn)方法
request方法解析
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
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;
}
1、這個過程主要是使用requestSerializer 調(diào)用requestWithMethod 或者 multipartFormRequestWithMethod 獲取
NSMutableURLRequest 類型的 request 對象。
使用指定的HTTP method和URLString來構(gòu)建一個NSMutableURLRequest對象實例,如果method是GET、HEAD、DELETE,那parameter將會被用來構(gòu)建一個基于url編碼的查詢字符串(query url),并且這個字符串會直接加到request的url后面。對于其他的Method,比如POST/PUT,它們會根據(jù)parameterEncoding屬性進行編碼,而后加到request的http body上。
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
在上面這個方法里設(shè)置了request的 請求方法,url,遍歷mutableObservedChangedKeyPaths中設(shè)置的屬性并給Request賦值,這里居然用了KVO。之前版本要遍歷AFHTTPRequestSerializerObservedKeyPaths()?,F(xiàn)在版本直接遍歷mutableObservedChangedKeyPaths,一大進步,之前就感覺這個設(shè)置很奇怪,終于修改了。
調(diào)用[mutableRequest setValue:value forHTTPHeaderField:field]方法設(shè)置請求頭相關(guān)信息。
非HTTPMethodsEncodingParametersInURI的請求先判斷request的Content-Type是否設(shè)置了,如果沒有,就默認設(shè)置為application/x-www-form-urlencoded。
在方法 multipartFormRequestWithMethod 中比 requestWithMethod 多了個參數(shù)為formData 的 block 對象參數(shù)。在方法體里面對formData 進行拼接。然后通過block傳出去。此處設(shè)計與Masonry框架中block使用方式相同,可以放一塊研究一下。
2、調(diào)用 dataTaskWithRequest 方法,使用上面的 request 生成 task 對象。
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
我要是沒記錯的話,3.0版本的時候在這里是用一個串行隊列創(chuàng)建dataTask,現(xiàn)在串行隊列相關(guān)代碼找不到了。用串行隊列來解決iOS8 的一個bug,可能現(xiàn)在已經(jīng)不用適配iOS8吧
addDelegateForDataTask 方法將初始化一個 AFURLSessionManagerTaskDelegate 對象,delegate 弱引用又持有了 manager, 打破閉環(huán)。在delegate中有 進度,回調(diào),存儲數(shù)據(jù)的mutableDate等屬性。
在addDelegateForDataTask 中調(diào)用 setDelegate:forTask 將task 和 delegate 綁定到一起。
在setDelegate:forTask 方法中 1、將delegate存入字典,以taskid作為key,說明每個task都有各自的代理; 2、設(shè)置兩個NSProgress的變量 - uploadProgress和downloadProgress,給session task添加了兩個KVO事件
設(shè)置這兩個NSProgress對應(yīng)的cancel、pause和resume這三個狀態(tài),正好對應(yīng)session task的cancel、suspend和resume三個狀態(tài)
當(dāng)NSURLSessionTask調(diào)用resume函數(shù)時,會postNotificationName:AFNSURLSessionTaskDidResumeNotification,從而執(zhí)行taskDidResume:方法
在 taskDidResume: 方法中會判斷task是否是當(dāng)前manager管理的,是的話會發(fā)送通知AFNetworkingTaskDidResumeNotification
3、task調(diào)用resume 執(zhí)行
resume 方法被 使用 method_exchangeImplementations交換為 af_resume方法。內(nèi)部發(fā)送了AFNSURLSessionTaskDidResumeNotification 通知。