上一篇整理了
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];,這一篇整理AFN的重頭戲:請(qǐng)求。從[manager POST:parameters:progress:success:failure:]開(kāi)始梳理。
AFHTTPSessionManager 部分
日常開(kāi)發(fā)中請(qǐng)求主要就是GET和POST兩種
GET 和 POST 的聲明
GET聲明
- (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;
POST聲明
- (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;
GET 和 POST 的實(shí)現(xiàn)
GET實(shí)現(xiàn)
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
POST實(shí)現(xiàn)
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
分解說(shuō)明
- 首先要理解,AFHTTPSessionManager類的GET和POST方法內(nèi)部實(shí)現(xiàn)的核心是:調(diào)用
dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:方法來(lái)獲取NSURLSessionDataTask。 - 這個(gè)NSURLSessionDataTask才是真正可以發(fā)起網(wǎng)絡(luò)請(qǐng)求的類。而開(kāi)啟請(qǐng)求的真正方法是
resume,當(dāng)NSURLSessionDataTask調(diào)用了resume,網(wǎng)絡(luò)請(qǐng)求才發(fā)起。
?
GET、POST 核心方法dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:

第一部分解析
一句話描述 : 第一部分做的事情就是創(chuàng)建一個(gè)NSMutableURLRequest。
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) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
先說(shuō)整體邏輯
- 第一部分創(chuàng)建的NSMutableURLRequest是要提供給第二部分使用。
- 這個(gè)NSMutableURLRequest不是AFHTTPSessionManager類自己創(chuàng)建的,是通過(guò)自己的屬性requestSerializer調(diào)用
requestWithMethod:URLString:parameters:error:方法創(chuàng)建的。如果NSMutableURLRequest創(chuàng)建失敗,則直接回調(diào)給開(kāi)發(fā)者網(wǎng)絡(luò)請(qǐng)求失敗。
分解說(shuō)明:
屬性requestSerializer:requestSerializer是AFHTTPSessionManager類自己獨(dú)有的屬性,父類AFHTTPRequestSerializer沒(méi)有這個(gè)屬性。requestSerializer在AFHTTPSessionManager初始化的時(shí)候已經(jīng)默認(rèn)進(jìn)行了初始化,開(kāi)發(fā)者也可以隨時(shí)修改此屬性。初始化詳情可以參考AFNetworking主線梳理(一)中的介紹。
參數(shù)completionQueue:失敗回調(diào)的隊(duì)列默認(rèn)是主隊(duì)列,如果設(shè)置了completionQueue,則走設(shè)置的隊(duì)列。completionQueue是AFHTTPSessionManager父類AFURLSessionManager的屬性,需要開(kāi)發(fā)者來(lái)設(shè)置。
?
接下來(lái),解析requestWithMethod:URLString:parameters:error:,需要跳轉(zhuǎn)到AFHTTPRequestSerializer 部分(往下翻)。
?
第二部分解析
一句話描述:
通過(guò)調(diào)用dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法來(lái)創(chuàng)建一個(gè)NSURLSessionDataTask實(shí)例對(duì)象。
__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);
}
}
}];
?
分解說(shuō)明
- NSURLSessionDataTask就是前面說(shuō)的GET和POST方法核心實(shí)現(xiàn)所需要的task。
-
dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法所需要的參數(shù)request就是第一部分創(chuàng)建的NSMutableURLRequest實(shí)例。 - AFHTTPSessionManager自己并沒(méi)有實(shí)現(xiàn)
dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法,是它的父類AFURLSessionManager實(shí)現(xiàn)了這個(gè)方法。
?
接下來(lái),解析dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法,跳轉(zhuǎn)到AFURLSessionManager 部分(使勁往下翻)。
?
AFHTTPRequestSerializer 部分
接續(xù) GET、POST 核心方法dataTaskWithHTTPMethod 方法第一部分,AFHTTPRequestSerializer的requestWithMethod:URLString:parameters:error:方法最終目的是創(chuàng)建一個(gè)NSMutableURLRequest實(shí)例并返回。
requestWithMethod:URLString:parameters:error: 方法解析
以下簡(jiǎn)稱:requestWithMethod方法。

requestWithMethod方法整體邏輯:
- 使用傳入的參數(shù)URLString創(chuàng)建一個(gè)NSURL *url
- 使用url創(chuàng)建一個(gè)NSMutableURLRequest *mutableRequest
- 將傳入的參數(shù) method 保存到 mutableRequest 的 HTTPMethod 屬性中
- 第一部分:簡(jiǎn)單講就是在配置NSMutableURLRequest的各種參數(shù)。
- 第二部分:簡(jiǎn)單講其實(shí)也是在配置NSMutableURLRequest的各種參數(shù)。
- NSMutableURLRequest配置完成后,返回其實(shí)例mutableRequest。
第一部分
整體邏輯:
-
AFHTTPRequestSerializerObservedKeyPaths()函數(shù)會(huì)創(chuàng)建并返回一個(gè)數(shù)組,數(shù)組中有6個(gè)屬性名的字符串(keyPath)。 - for in 遍歷這個(gè)數(shù)組,取出每一個(gè)屬性名的字符串(keyPath)。
- 判斷
self.mutableObservedChangedKeyPaths(一個(gè)NSMutableSet)中是否包含keyPath,如果包含這個(gè)keyPath,則把AFHTTPRequestSerializer自己這個(gè)屬性的值賦值給mutableRequest。
分解說(shuō)明:
-
AFHTTPRequestSerializer有6個(gè)特定的屬性,分別是:
@property (nonatomic, assign) BOOL allowsCellularAccess;@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;@property (nonatomic, assign) NSTimeInterval timeoutInterval;
這6個(gè)屬性開(kāi)發(fā)者可以讀寫(xiě),而這6個(gè)屬性與NSMutableURLRequest的屬性一一對(duì)應(yīng),換句話說(shuō)AFHTTPRequestSerializer的6個(gè)屬性最終目的是開(kāi)發(fā)者設(shè)置具體值,然后傳遞給NSMutableURLRequest,只不過(guò)開(kāi)發(fā)者從設(shè)置值到mutableRequest接收到值的過(guò)程略顯復(fù)雜。
在AFHTTPRequestSerializer的
init方法中依次對(duì)6個(gè)屬性添加了鍵值觀察(KVO)。
init方法片段:
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
...省略...
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
return self;
}
?
- AFHTTPRequestSerializer重寫(xiě)了每一個(gè)屬性的setter,成員變量Ivar賦值前后加了KVO的
willChangeValueForKey和didChangeValueForKey來(lái)手動(dòng)觸發(fā)KVO。
AFHTTPRequestSerializer的6個(gè)屬性的setter實(shí)現(xiàn)方式完全一致,這里只貼兩個(gè)屬性的setter源碼:
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
[self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
_allowsCellularAccess = allowsCellularAccess;
[self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}
- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy {
[self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
_cachePolicy = cachePolicy;
[self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
}
等等......
?
-
AFHTTPRequestSerializerObservedKeyPaths()函數(shù):
下的源碼就是AFHTTPRequestSerializerObservedKeyPaths()函數(shù)的實(shí)現(xiàn),其實(shí)很簡(jiǎn)單,只是創(chuàng)建了一個(gè)單例數(shù)組,數(shù)組中是這個(gè)6個(gè)屬性的getter由selector轉(zhuǎn)成的string。
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}
?
-
self.mutableObservedChangedKeyPaths解析-
self.mutableObservedChangedKeyPaths是什么?- 答:AFHTTPRequestSerializer的一個(gè)屬性。用來(lái)保存被開(kāi)發(fā)者賦值的6個(gè)屬性中的一個(gè)或多個(gè)(也可以沒(méi)有)。
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths; -
self.mutableObservedChangedKeyPaths是在什么地方被初始化的?- 答:是在AFHTTPRequestSerializer的
init方法中被初始化的。
self.mutableObservedChangedKeyPaths = [NSMutableSet set]; - 答:是在AFHTTPRequestSerializer的
- 案例Demo中并沒(méi)有調(diào)用AFHTTPRequestSerializer的
init方法,AFHTTPRequestSerializer的init方法是什么時(shí)候調(diào)用的?- 答:完整的調(diào)用順序是:
[AFHTTPSessionManager manager]=>[AFHTTPRequestSerializer serializer]=>[[self alloc] init]。這里self是指AFHTTPRequestSerializer。
- 答:完整的調(diào)用順序是:
-
self.mutableObservedChangedKeyPaths在什么時(shí)候添加的元素?- 答:繼續(xù)往下看。
-
?
第一部分總結(jié):
AFHTTPRequestSerializer早在初始化的時(shí)候就對(duì)6個(gè)屬性依次添加了鍵值觀察(KVO)。如果我們?cè)谡{(diào)用 AFHTTPSessionManager 的 GET 方法或者 POST 方法之前對(duì)這6個(gè)屬性賦了值,就會(huì)調(diào)用屬性的setter,接著會(huì)觸發(fā)KVO的通知,來(lái)到KVO的observeValueForKeyPath:ofObject:change:context:回調(diào)方法。
#pragma mark - NSKeyValueObserving
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
在KVO的observeValueForKeyPath:ofObject:change:context:回調(diào)方法中,根據(jù)值是否為空,來(lái)操作self.mutableObservedChangedKeyPaths是添加該keyPath還是移除。
我們假定開(kāi)發(fā)者在調(diào)用GET或POST方法請(qǐng)求前設(shè)置了timeoutInterval屬性:
- 走timeoutInterval屬性的setter
- 觸發(fā)KVO的通知,來(lái)到KVO的
observeValueForKeyPath:ofObject:change:context:回調(diào)方法 - timeoutInterval屬性被添加到了mutableObservedChangedKeyPaths集合中
- 接下來(lái)就是調(diào)用AFHTTPSessionManager的 GET 或 POST 方法發(fā)起請(qǐng)求
- 然后就會(huì)來(lái)到此處(requestWithMethod:URLString:parameters:error:方法的第一部分,就是現(xiàn)在在解析的這里)
- 接著把timeoutInterval賦值給NSMutableURLRequest。
以上就是第一部分做的全部事情。
?
一些補(bǔ)充:
-
automaticallyNotifiesObserversForKey:函數(shù)用來(lái)根據(jù)條件阻止KVO發(fā)出通知,系統(tǒng)提供的方法,AFN只是進(jìn)行了重寫(xiě)。 - 集合(set)自帶去重的功能,所以當(dāng)6個(gè)屬性被多次賦值時(shí),也就是多次觸發(fā)observeValueForKeyPath,mutableObservedChangedKeyPaths集合(set)中不會(huì)出現(xiàn)多個(gè)同樣的keyPath。
為什么AFN要手動(dòng)觸發(fā)KVO,而不是自動(dòng)觸發(fā)KVO ?
答:AFN的早期版本也使用KVO觀察這6個(gè)屬性,有人發(fā)現(xiàn)在單元測(cè)試中會(huì)出現(xiàn)崩潰,所以后來(lái)增加了automaticallyNotifiesObserversForKey方法來(lái)阻止這6個(gè)屬性通過(guò)KVO自動(dòng)發(fā)送通知。但是很快人們發(fā)現(xiàn)這樣做,這6個(gè)屬性使用開(kāi)始出現(xiàn)問(wèn)題,會(huì)有設(shè)置后無(wú)效的情況。于是作者又改了回去,刪了automaticallyNotifiesObserversForKey方法,但是問(wèn)題又繞回原地了。因此為了能在單元測(cè)試中正常使用,并且這6個(gè)屬性的使用不能受到影響,即正常可以使用KVO,于是增加了6個(gè)屬性的setter,在setter里面手動(dòng)觸發(fā)KVO。
?
第二部分 (高能預(yù)警)
一句話描述:
根據(jù)請(qǐng)求方法(GET、POST)的不同,將Parameters按照每種方法的要求組裝到NSMutableURLRequest的實(shí)例mutableRequest中去。即GET方法是將參數(shù)處理后拼接到URL中,POST方法是將參數(shù)處理后設(shè)置到請(qǐng)求體中。
第二部分核心方法:requestBySerializingRequest:withParameters:error:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 這部分起個(gè)名:self.HTTPRequestHeaders部分
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
// 這部分起個(gè)名:處理傳入的parameters部分
NSString *query = nil;
if (parameters) {
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// 這部分起個(gè)名:使用query裝配mutableRequest部分
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
?
在源碼的注釋中為了方便敘述我給劃分成了3個(gè)部分,分別是:
- self.HTTPRequestHeaders部分
- 處理傳入的parameters部分
- 使用query裝配mutableRequest部分
按照代碼順序,逐步解析:
self.HTTPRequestHeaders部分
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
一句話描述:
遍歷self.HTTPRequestHeaders,如果我們創(chuàng)建的新 NSURLRequest *request 中沒(méi)有開(kāi)發(fā)者指定(init中也指定了默認(rèn))的請(qǐng)求頭字段和值,那么把沒(méi)有的這部分開(kāi)發(fā)者指定的的請(qǐng)求頭字段和值保存一份到request中。
整體邏輯
- 遍歷self.HTTPRequestHeaders
- self.HTTPRequestHeaders只有初始化時(shí)AFN默認(rèn)添加的
Accept-Language、User-Agent兩個(gè)鍵值對(duì)。
- self.HTTPRequestHeaders只有初始化時(shí)AFN默認(rèn)添加的
- 如果我們創(chuàng)建的新 NSURLRequest *request 中開(kāi)發(fā)者指定了
Accept-Language和User-Agent以外的請(qǐng)求頭鍵值對(duì),那么把這些Accept-Language與User-Agent以外的請(qǐng)求頭鍵值對(duì)保存一份到request中。
分解說(shuō)明
-
self.HTTPRequestHeaders 的聲明在 AFHTTPRequestSerializer.h:
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders; -
self.HTTPRequestHeaders 的取值方法,即getter:
- (NSDictionary *)HTTPRequestHeaders { NSDictionary __block *value; dispatch_sync(self.requestHeaderModificationQueue, ^{ value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; }); return value; }getter解析:
- self.HTTPRequestHeaders 的值完全來(lái)自于 self.mutableHTTPRequestHeaders
- requestHeaderModificationQueue 在 AFHTTPRequestSerializer 的 init 方法中初始化,是一個(gè)并發(fā)隊(duì)列。具體請(qǐng)參考AFNetworking主線梳理(一)AFHTTPRequestSerializer 部分。
self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT); -
self.mutableHTTPRequestHeaders
- self.mutableHTTPRequestHeaders 的聲明在 AFHTTPRequestSerializer.m:
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;- self.mutableHTTPRequestHeaders 在 AFHTTPRequestSerializer 的 init 方法中初始化
self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];- self.mutableHTTPRequestHeaders 如何被賦值?
- self.mutableHTTPRequestHeaders 是在
setValue:forHTTPHeaderField:方法中間接賦值的。 - requestHeaderModificationQueue 在 AFHTTPRequestSerializer 的 init 方法中初始化,和前面第2小點(diǎn)的"self.HTTPRequestHeaders取值方法"用的是同一個(gè)并發(fā)隊(duì)列。具體請(qǐng)參考AFNetworking主線梳理(一)AFHTTPRequestSerializer 部分。
- self.mutableHTTPRequestHeaders 是在
- 那么 self.mutableHTTPRequestHeaders 是什么時(shí)候被賦上默認(rèn)值的呢?
- 答:在 AFHTTPRequestSerializer的init 方法中,分兩次給 self.mutableHTTPRequestHeaders 賦了默認(rèn)值,分別是:Accept-Language、User-Agent。具體請(qǐng)參考AFNetworking主線梳理(一)AFHTTPRequestSerializer 部分。
?
- 答:在 AFHTTPRequestSerializer的init 方法中,分兩次給 self.mutableHTTPRequestHeaders 賦了默認(rèn)值,分別是:Accept-Language、User-Agent。具體請(qǐng)參考AFNetworking主線梳理(一)AFHTTPRequestSerializer 部分。
至此,“第二部分 (高能預(yù)警)” => “self.HTTPRequestHeaders部分”的使命完成。
問(wèn):開(kāi)發(fā)者如何給request指定請(qǐng)求頭的字段和值呢?
答:通過(guò) AFHTTPRequestSerializer 的setValue:forHTTPHeaderField:方法。
處理傳入的parameters部分
NSString *query = nil;
if (parameters) {
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
一句話描述:“處理傳入的parameters部分” 就是將parameters按照規(guī)則拼接成query string。
假定開(kāi)發(fā)者傳入的parameters為字典{"key1" : "value1", "key2" : "value2"},那么query = "key1=value1&key2=value2"。
接下來(lái)我們看看parameters是如何被拼接成query的。
“處理傳入的parameters部分” 按照 if else被自然分割成兩部分,先看 if 這部分。
if 分支整體邏輯:
- 如果 self.queryStringSerialization(一個(gè)block)存在,即開(kāi)發(fā)者實(shí)現(xiàn)了這個(gè)block,則執(zhí)行
if分支內(nèi)的語(yǔ)句。 - 把 request、parameters 以及 *error 傳遞給self.queryStringSerialization這個(gè)block,由block處理完成后,返回query。
分解說(shuō)明:
- self.queryStringSerialization 是一個(gè)block,需要開(kāi)發(fā)者實(shí)現(xiàn),聲明以及賦值方法 API 如下
// AFURLRequestSerialization.h
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
// AFURLRequestSerialization.m
typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error);
@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block {
self.queryStringSerialization = block;
}
self.queryStringSerialization 是什么時(shí)候、什么地方被實(shí)現(xiàn)的?
答:沒(méi)有默認(rèn)的初始化,需要開(kāi)發(fā)者去實(shí)現(xiàn)此block。如果開(kāi)發(fā)者沒(méi)有實(shí)現(xiàn)這個(gè)block,就不會(huì)走if分支。self.queryStringSerialization 的作用
答:這個(gè)block的意義所在,是當(dāng)開(kāi)發(fā)者如果需要自定義 query string 的拼接規(guī)則,則用block傳過(guò)來(lái)的request、parameters 以及 error 自行拼接一個(gè) query string 并返回。NSError *__autoreleasing *error 干啥的?
答案:如果開(kāi)發(fā)者在拼接 query string 過(guò)程中出現(xiàn)錯(cuò)誤,想要拋出Error,就可以利用這個(gè)指針的指針來(lái)直接傳遞error。
?
else 分支整體邏輯:
可以說(shuō)
else分支直接可以看成query = AFQueryStringFromParameters(parameters);這一句代碼最為合適。
如果開(kāi)發(fā)者沒(méi)有使用(實(shí)現(xiàn))self.queryStringSerialization,則會(huì)走else分支,else分支也是最普通、最常用的分支。
分解說(shuō)明:
- 雖然
else分支是一整個(gè)switch控制的,但是AFHTTPRequestQueryStringSerializationStyle枚舉值只有一個(gè)而且還是 0 (NSUInteger)。也就是說(shuō)開(kāi)發(fā)者設(shè)置這個(gè)Style也好不設(shè)置也好,最終都會(huì)走switch的第一個(gè)case分支,目前AFN的版本中你沒(méi)得選。
typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
AFHTTPRequestQueryStringDefaultStyle = 0,
};
- self.queryStringSerializationStyle 是一個(gè)枚舉值,需要開(kāi)發(fā)者指定,聲明以及賦值方法 API 如下
// AFURLRequestSerialization.h
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;
// AFURLRequestSerialization.m
@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style {
self.queryStringSerializationStyle = style;
self.queryStringSerialization = nil;
}
?
重點(diǎn)?。。?/strong>
else 分支的核心函數(shù) AFQueryStringFromParameters()
-
AFQueryStringFromParameters()函數(shù)
一句話描述:按照規(guī)則把parameters中所有的key和value拼接成一個(gè)字符串。
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
?
詳細(xì)邏輯:
- 創(chuàng)建一個(gè)空的可變數(shù)組,NSMutableArray *mutablePairs
- 調(diào)用
AFQueryStringPairsFromDictionary()函數(shù)獲得一個(gè)數(shù)組,一個(gè)裝滿AFQueryStringPair實(shí)例對(duì)象的數(shù)組。AFQueryStringPair是AFURLRequestSerialization的私有類,具體實(shí)現(xiàn)貼在文末(往下翻)。
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
-
AFQueryStringPairsFromDictionary()函數(shù)直接調(diào)用AFQueryStringPairsFromKeyAndValue()函數(shù)
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
// 升序排序
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else { // 這個(gè)分支才是主要分支
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
-
AFQueryStringPairsFromKeyAndValue()函數(shù)采用遞歸調(diào)用方式將傳進(jìn)來(lái)的value(假定是字典)一層一層剝開(kāi),直到value不再是任何類型的集合,然后組裝成AFQueryStringPair實(shí)例對(duì)象添加到數(shù)組中,返回?cái)?shù)組。- 4.1 如果傳進(jìn)來(lái)的value是一個(gè)字典套字典的結(jié)構(gòu):
value = {data : {adSource : Admob}}。如果傳進(jìn)來(lái)的key是空,則第一次遞歸不重新組裝函數(shù)的參數(shù)key,直接用下一層的key當(dāng)做函數(shù)遞歸時(shí)的key。 - 4.2 假定傳進(jìn)來(lái)的
key="Hello"。 - 4.3 先把value的allKeys進(jìn)行升序排序,形成一個(gè)新的數(shù)組。
- 4.4
for in遍歷這個(gè)新的數(shù)組,取出每一個(gè)key(data這一層),使用這個(gè)key從字典取出{adSource : Admob},即先把value這個(gè)字典剝開(kāi)一層。 - 4.5 重新組裝
AFQueryStringPairsFromKeyAndValue(NSString *key, id value)函數(shù)的參數(shù),進(jìn)行遞歸。 - 4.6 若函數(shù)傳進(jìn)來(lái)的key為Hello,則遞歸時(shí)
key="Hello[data]",value={adSource : Admob}。若函數(shù)傳進(jìn)來(lái)的key為空,則遞歸時(shí)key="data",value={adSource : Admob}。 - 4.7 遞歸到最后,會(huì)走到
AFQueryStringPairsFromKeyAndValue()函數(shù)的最后一個(gè)分支,即不是任何一個(gè)集合的else分支里,此例的最終key="Hello[data][adSource]"或者"data[adSource]",最終的value="Admob",用這個(gè)鍵值對(duì)初始化一個(gè)AFQueryStringPair實(shí)例對(duì)象,添加到數(shù)組,返回此數(shù)組。
- 4.1 如果傳進(jìn)來(lái)的value是一個(gè)字典套字典的結(jié)構(gòu):
- 經(jīng)過(guò)3和4已經(jīng)獲得了2所需要的裝滿
AFQueryStringPair實(shí)例對(duì)象的數(shù)組,for in遍歷這個(gè)數(shù)組中的每一個(gè)AFQueryStringPair實(shí)例對(duì)象,將[AFQueryStringPair URLEncodedStringValue]的返回值添加到數(shù)組。 - 第5步的數(shù)組組裝完成后,大致應(yīng)該是這樣
("data[adSource]=Admob", "data[adType]=3")。然后將這個(gè)數(shù)組使用"&"拆分組裝成字符串,返回這個(gè)字符串,這個(gè)字符串就是query string。這個(gè)query大致長(zhǎng)這樣:"data[adSource]=Admob& data[adType]=3"。 - query組裝完成,
AFQueryStringFromParameters()函數(shù)完成使命,返回query
至此,
AFQueryStringFromParameters()函數(shù)的使命已完成,返回了拼接好的query。
??把文章往上翻??
這個(gè)query由else分支的query接收,也就是說(shuō)else分支的使命也已完成。
??再把文章往上翻??
而if分支是由開(kāi)發(fā)者實(shí)現(xiàn)的self.queryStringSerializationblock來(lái)完成query的拼接。if和else分支都可完成query的拼接。
??再一次把文章往上翻??
“第二部分 (高能預(yù)警)” => “處理傳入的parameters部分”就全部完成了,接下來(lái)該說(shuō)一說(shuō)“第二部分 (高能預(yù)警)” => “使用query裝配mutableRequest部分”
使用query裝配mutableRequest部分
一句話描述:將query裝配到mutableRequest里。
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
詳細(xì)邏輯:
這一部分仍然是
if else天然分割成的兩個(gè)分支。判斷條件是:self.HTTPMethodsEncodingParametersInURI(NSSet)里是否包含[request HTTPMethod]的請(qǐng)求方法。if分支:如果[request HTTPMethod]的方法是GET、DELETE或HEAD這三種,則屬于包含在里面。那么把請(qǐng)求時(shí)傳進(jìn)來(lái)的URL拆分,連同query一起,添加進(jìn)去,將URL重新組裝,再賦值給mutableRequest.URL,比如GET方法的請(qǐng)求參數(shù)就是拼接在URL里面。else分支:如果[request HTTPMethod]的方法是POST,則不包含在里面。那么就把query按照UTF8編碼成data后,調(diào)用[mutableRequest setHTTPBody:]將POST的請(qǐng)求參數(shù)(query)設(shè)置到請(qǐng)求體中。
?
分解說(shuō)明:
- self.HTTPMethodsEncodingParametersInURI 是一個(gè)集合(NSSet),AFN給了默認(rèn)值
GET,HEAD,DELETE。開(kāi)發(fā)者也可以自己指定。頭文件里聲明,AFJSONRequestSerializer 的init方法設(shè)置的默認(rèn)值:
// AFURLRequestSerialization.h
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;
// AFURLRequestSerialization.m => init 方法
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
[request HTTPMethod]是什么?
答:[request HTTPMethod]方法返回的是開(kāi)發(fā)者設(shè)置到request的請(qǐng)求方法,諸如 GET、POST 等。-
request的請(qǐng)求方法是什么時(shí)候設(shè)置的?
答:完整的調(diào)用順序是- AFHTTPSessionManager:GET或者POST方法
- AFHTTPSessionManager:[dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:]方法
- AFHTTPRequestSerializer:[requestWithMethod:URLString:parameters:error:] 方法
- AFHTTPRequestSerializer:mutableRequest.HTTPMethod = method;
self.stringEncoding 是什么?
答:字符串編碼,默認(rèn)為NSUTF8StringEncoding。self.stringEncoding 是什么時(shí)候設(shè)置的默認(rèn)值?
答:AFHTTPRequestSerializer 的init方法中。
self.stringEncoding = NSUTF8StringEncoding;
至此,“第二部分 (高能預(yù)警)” => “使用query裝配mutableRequest部分” 使命已完成,已成功的將query string裝配到mutableRequest里。
同時(shí)也意味著“第二部分 (高能預(yù)警)”這一部分使命也已完成。
再加上第一部分,[requestWithMethod:URLString:parameters:error:]方法的使命也已完成,創(chuàng)建一個(gè)NSMutableURLRequest實(shí)例,并返回給 AFHTTPSessionManager 的[dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:]方法。
AFHTTPRequestSerializer 部分使命全部完成。AFHTTPRequestSerializer 部分已經(jīng)負(fù)責(zé)創(chuàng)建 NSURLRequest 并返回給 AFHTTPSessionManager 的dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:第一部分。
AFURLSessionManager 部分
接續(xù) AFHTTPSessionManager 的 dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure: 第二部分,AFURLSessionManager 部分的dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:函數(shù)主要任務(wù)就是創(chuàng)建一個(gè) NSURLSessionDataTask 實(shí)例對(duì)象,并返回。
dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: 函數(shù)解析
- (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 {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
整體邏輯
- 創(chuàng)建一個(gè)局域變量:NSURLSessionDataTask *dataTask,初始化為 nil 。
- 實(shí)現(xiàn)
url_session_manager_create_task_safely函數(shù)的block。 - 在block的回調(diào)中,通過(guò)屬性 session 調(diào)用
dataTaskWithRequest:方法。 -
dataTaskWithRequest:方法會(huì)創(chuàng)建一個(gè) NSURLSessionDataTask 實(shí)例對(duì)象,并返回。 - 調(diào)用
addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:方法(參考 AFNetworking主線梳理(一))。
分解說(shuō)明
-
url_session_manager_create_task_safely函數(shù)- 函數(shù)的實(shí)現(xiàn)
static void url_session_manager_create_task_safely(dispatch_block_t block) { if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) { // Fix of bug // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8) // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093 dispatch_sync(url_session_manager_creation_queue(), block); } else { block(); } }- 函數(shù)的主要邏輯:
只有if else, 如果版本號(hào)小于iOS修復(fù)版本號(hào),則需要把當(dāng)前任務(wù)同步的添加到一個(gè)串行隊(duì)列中,保證不發(fā)生并發(fā)。否則,直接調(diào)用block回調(diào)回去。 - 函數(shù)的block回調(diào):
函數(shù)的block回調(diào)中的代碼最終還是順序執(zhí)行的,無(wú)論是在if還是else里都是同步順序執(zhí)行。簡(jiǎn)單講就是dataTask = [self.session dataTaskWithRequest:request];這句代碼是順序執(zhí)行的,就執(zhí)行順序而言與在block外無(wú)異。 - 函數(shù)的參數(shù):
url_session_manager_creation_queue()函數(shù)創(chuàng)建并返回一個(gè)類似單例管理的串行隊(duì)列。
static dispatch_queue_t url_session_manager_creation_queue() { static dispatch_queue_t af_url_session_manager_creation_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); }); return af_url_session_manager_creation_queue; }
?
- self.session
- session是AFURLSessionManager的屬性
@property (readonly, nonatomic, strong) NSURLSession *session;- session屬性是什么時(shí)候初始化的?
在AFURLSessionManager初始化方法initWithSessionConfiguration:中進(jìn)行的初始化,由AFN指定默認(rèn)配置。具體請(qǐng)參考AFNetworking主線梳理(一)。
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
?
-
dataTaskWithRequest:request是 NSURLSession 的方法,利用入?yún)SURLRequest創(chuàng)建一個(gè) NSURLSessionDataTask 實(shí)例對(duì)象并返回。
?
問(wèn):為什么創(chuàng)建dataTask的代碼要在
url_session_manager_create_task_safely函數(shù)的block回調(diào)中執(zhí)行?
答:為了解決 iOS 7 存在的bug。在 iOS 7 某些版本中如果并發(fā)創(chuàng)建NSURLSessionTask,會(huì)造成 NSURLSessionTask 的 taskIdentifier不唯一(有重復(fù)的),而AFN依賴 taskIdentifier 保存了很多信息,會(huì)造成混亂。
問(wèn):為什么
url_session_manager_creation_queue()函數(shù)用單例保存隊(duì)列?
個(gè)人猜測(cè):因?yàn)樵撽?duì)列職責(zé)單一,無(wú)需多次重復(fù)創(chuàng)建新隊(duì)列。網(wǎng)絡(luò)請(qǐng)求多數(shù)情況下是高頻的,所以多次創(chuàng)建不是一個(gè)好的選擇。
AFQueryStringPair 部分
#pragma mark -
@interface AFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
- (instancetype)initWithField:(id)field value:(id)value;
- (NSString *)URLEncodedStringValue;
@end
@implementation AFQueryStringPair
- (instancetype)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
self.field = field;
self.value = value;
return self;
}
- (NSString *)URLEncodedStringValue {
if (!self.value || [self.value isEqual:[NSNull null]]) {
return AFPercentEscapedStringFromString([self.field description]);
} else {
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
}
}
@end
AFPercentEscapedStringFromString()沒(méi)什么特殊,就是把傳進(jìn)去的字符串做了百分號(hào)編碼的處理,防止網(wǎng)絡(luò)請(qǐng)求時(shí)出現(xiàn)不符合規(guī)則的字符。處理完成后,返回安全的字符串。