接下來(lái)我們來(lái)補(bǔ)充之前AFURLResponseSerialization這一塊是如何解析數(shù)據(jù)的
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
/**
The response object decoded from the data associated with a specified response.
@param response The response to be processed.
@param data The response data to be decoded.
@param error The error that occurred while attempting to decode the response data.
@return The object decoded from the specified response data.
*/
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
這實(shí)際是一個(gè)協(xié)議方法涩哟。而后面的解析類都是遵守這個(gè)協(xié)議方法赡麦,去做數(shù)據(jù)解析
AFHTTPResponseSerializer:
- (id)responseObjectForResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
return [[NSXMLParser alloc] initWithData:data];
}
方法調(diào)用了一個(gè)另外的方法之后,就把data返回來(lái)了
// 判斷是不是可接受類型和可接受code娩怎,不是則填充error
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
// response是否合法標(biāo)識(shí)
BOOL responseIsValid = YES;
// 驗(yàn)證的error
NSError *validationError = nil;
// 如果存在且是NSHTTPURLResponse
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
//主要判斷自己能接受的數(shù)據(jù)類型和response的數(shù)據(jù)類型是否匹配吨岭,
//如果有接受數(shù)據(jù)類型,如果不匹配response峦树,而且響應(yīng)類型不為空辣辫,數(shù)據(jù)長(zhǎng)度不為0
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
//進(jìn)入If塊說(shuō)明解析數(shù)據(jù)肯定是失敗的,這時(shí)候要把解析錯(cuò)誤信息放到error里魁巩。
//如果數(shù)據(jù)長(zhǎng)度大于0急灭,而且有響應(yīng)url
if ([data length] > 0 && [response URL]) {
// 錯(cuò)誤信息字典,填充一些錯(cuò)誤信息
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
//生成錯(cuò)誤
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
//返回標(biāo)識(shí)
responseIsValid = NO;
}
//判斷自己可接受的狀態(tài)嗎
//如果和response的狀態(tài)碼不匹配谷遂,則進(jìn)入if塊
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
//填寫(xiě)錯(cuò)誤信息字典
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
//生成錯(cuò)誤
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
//返回標(biāo)識(shí)
responseIsValid = NO;
}
}
//給我們傳過(guò)來(lái)的錯(cuò)誤指針賦值
if (error && !responseIsValid) {
*error = validationError;
}
//返回是否錯(cuò)誤標(biāo)識(shí)
return responseIsValid;
}
●簡(jiǎn)單來(lái)說(shuō)葬馋,這個(gè)方法就是來(lái)判斷返回?cái)?shù)據(jù)與咱們使用的解析器是否匹配,需要解析的狀態(tài)碼是否匹配肾扰。如果錯(cuò)誤畴嘶,則填充錯(cuò)誤信息,并且返回NO集晚,否則返回YES窗悯,錯(cuò)誤信息為nil。
●其中里面出現(xiàn)了兩個(gè)屬性值偷拔,一個(gè)acceptableContentTypes蒋院,一個(gè)acceptableStatusCodes,兩者在初始化的時(shí)候有給默認(rèn)值莲绰,我們也可以去自定義欺旧,但是如果給acceptableContentTypes定義了不匹配的類型,那么數(shù)據(jù)仍舊會(huì)解析錯(cuò)誤蛤签。
●而AFHTTPResponseSerializer僅僅是調(diào)用驗(yàn)證方法辞友,然后就返回了data。
AFJSONResponseSerializer:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
//先判斷是不是可接受類型和可接受code
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
//error為空,或者有錯(cuò)誤称龙,去函數(shù)里判斷留拾。
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
//返回空
return nil;
}
}
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
//如果數(shù)據(jù)為空
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
//空則返回nil
if (data.length == 0 || isSpace) {
return nil;
}
NSError *serializationError = nil;
// 不為空解析Jason
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject)
{
// 拿著json解析的error去填充錯(cuò)誤信息
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
//判斷是否需要移除Null值
if (self.removesKeysWithNullValues) {
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
//返回解析結(jié)果
return responseObject;
}
接下來(lái)我們接著看調(diào)用的第一個(gè)方法
// 判斷是不是我們自己之前生成的錯(cuò)誤信息,是的話返回YES
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
// 判斷錯(cuò)誤域名和傳過(guò)來(lái)的域名是否一致茵瀑,錯(cuò)誤code是否一致
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
}
//如果userInfo的NSUnderlyingErrorKey有值间驮,則在判斷一次躬厌。
else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
這里可以注意马昨,我們這里傳過(guò)去的code和domain兩個(gè)參數(shù)分別為NSURLErrorCannotDecodeContentData、AFURLResponseSerializationErrorDomain扛施,這兩個(gè)參數(shù)是我們之前判斷response可接受類型和code時(shí)候自己去生成錯(cuò)誤的時(shí)候填寫(xiě)的鸿捧。
調(diào)用第二個(gè)方法
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
//分?jǐn)?shù)組和字典
if ([JSONObject isKindOfClass:[NSArray class]]) {
//生成一個(gè)數(shù)組
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
//調(diào)用自己
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
//看我們解析類型是mutable還是非muatable,返回mutableArray或者array
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
id value = (NSDictionary *)JSONObject[key];
//value空則移除
if (!value || [value isEqual:[NSNull null]]) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
//如果數(shù)組還是去調(diào)用自己
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
}
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return JSONObject;
}
方法主要還是通過(guò)遞歸的形式實(shí)現(xiàn)。比較簡(jiǎn)單疙渣。
第三個(gè)
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
方法主要是把json解析的錯(cuò)誤匙奴,賦值給我們需要返回給用戶的error上。比較簡(jiǎn)單
至此妄荔,AFJSONResponseSerializer就講完了
現(xiàn)在我們回到一開(kāi)始初始化的這行代碼上:
self.operationQueue.maxConcurrentOperationCount = 1;
首先我們要明確一個(gè)概念泼菌,這里的并發(fā)數(shù)僅僅是回調(diào)代理的線程并發(fā)數(shù)。而不是請(qǐng)求網(wǎng)絡(luò)的線程并發(fā)數(shù)啦租。請(qǐng)求網(wǎng)絡(luò)是由NSURLSession來(lái)做的哗伯,它內(nèi)部維護(hù)了一個(gè)線程池,用來(lái)做網(wǎng)絡(luò)請(qǐng)求篷角。它調(diào)度線程,基于底層的CFSocket去發(fā)送請(qǐng)求和接收數(shù)據(jù)焊刹。這些線程是并發(fā)的。
明確了這個(gè)概念之后恳蹲,我們來(lái)梳理一下AF的整個(gè)流程和線程的關(guān)系:
● 一開(kāi)始初始化sessionManager的時(shí)候虐块,一般都是在主線程。
● 然后我們調(diào)用get
或者post
等去請(qǐng)求數(shù)據(jù)嘉蕾,接著會(huì)進(jìn)行request
拼接贺奠,AF代理的字典映射,progress
的KVO
添加等等错忱,到NSUrlSession
的resume
之前這些準(zhǔn)備工作敞嗡,仍舊是在主線程中的。
● 然后我們調(diào)用NSUrlSession
的resume
航背,接著就跑到NSUrlSession
內(nèi)部去對(duì)網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)請(qǐng)求了,在它內(nèi)部是多線程并發(fā)的去請(qǐng)求數(shù)據(jù)的喉悴。
● 緊接著數(shù)據(jù)請(qǐng)求完成后,回調(diào)回來(lái)在我們一開(kāi)始生成的并發(fā)數(shù)為1的NSOperationQueue
中玖媚,這個(gè)時(shí)候會(huì)是多線程串行的回調(diào)回來(lái)的箕肃。
● 然后我們到返回?cái)?shù)據(jù)解析那一塊,我們自己又創(chuàng)建了并發(fā)的多線程今魔,去對(duì)這些數(shù)據(jù)進(jìn)行了各種類型的解析勺像。
●最后我們?nèi)绻凶远x的completionQueue
障贸,則在自定義的queue
中回調(diào)回來(lái),也就是分線程回調(diào)回來(lái)吟宦,否則就是主隊(duì)列篮洁,主線程中回調(diào)結(jié)束。
最后我們來(lái)解釋解釋為什么回調(diào)Queue要設(shè)置并發(fā)數(shù)為1:
我認(rèn)為AF這么做有以下兩點(diǎn)原因:
1.眾所周知殃姓,AF2.x所有的回調(diào)是在一條線程袁波,這條線程是AF的常駐線程,而這一條線程正是AF調(diào)度request的思想精髓所在蜗侈,所以第一個(gè)目的就是為了和之前版本保持一致篷牌。
2.因?yàn)楦硐嚓P(guān)的一些操作AF都使用了NSLock。所以就算Queue的并發(fā)數(shù)設(shè)置為n踏幻,因?yàn)槎嗑€程回調(diào)枷颊,鎖的等待,導(dǎo)致所提升的程序速度也并不明顯该面。反而多task回調(diào)導(dǎo)致的多線程并發(fā)夭苗,平白浪費(fèi)了部分性能。
而設(shè)置Queue的并發(fā)數(shù)為1隔缀,(注:這里雖然回調(diào)Queue的并發(fā)數(shù)為1题造,仍然會(huì)有不止一條線程,但是因?yàn)槭谴谢卣{(diào)蚕泽,所以同一時(shí)間晌梨,只會(huì)有一條線程在操作AFUrlSessionManager的那些方法。)至少回調(diào)的事件须妻,是不需要多線程并發(fā)的仔蝌。回調(diào)沒(méi)有了NSLock的等待時(shí)間荒吏,所以對(duì)時(shí)間并沒(méi)有多大的影響敛惊。(注:但是還是會(huì)有多線程的操作的,因?yàn)樵O(shè)置剛開(kāi)始調(diào)起請(qǐng)求的時(shí)候绰更,是在主線程的瞧挤,而回調(diào)則是串行分線程。)
參考資料:
AFNetworking到底做了什么
結(jié)合最新的AF源碼做了適當(dāng)?shù)男薷?br>
文章所用到的注釋后的源碼