版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.03.01 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請(qǐng)求言询,都離不開一個(gè)非常有用的框架AFNetworking,可以說這個(gè)框架的知名度已經(jīng)超過了蘋果的底層網(wǎng)絡(luò)請(qǐng)求部分想际,很多人可能不知道蘋果底層是如何發(fā)起網(wǎng)絡(luò)請(qǐng)求的庸推,但是一定知道
AFNetworking
蒜茴,接下來幾篇我們就一起詳細(xì)的解析一下這個(gè)框架。感興趣的可以看上面寫的幾篇疆栏。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請(qǐng)求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
3. AFNetworking源碼探究(三) —— GET請(qǐng)求實(shí)現(xiàn)之任務(wù)進(jìn)度設(shè)置和通知監(jiān)聽(一)
4. AFNetworking源碼探究(四) —— GET請(qǐng)求實(shí)現(xiàn)之代理轉(zhuǎn)發(fā)思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細(xì)解析(一)
6. AFNetworking源碼探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate詳細(xì)解析(一)
7. AFNetworking源碼探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate詳細(xì)解析(一)
8. AFNetworking源碼探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate詳細(xì)解析(一)
9. AFNetworking源碼探究(九) —— AFURLSessionManagerTaskDelegate中三個(gè)轉(zhuǎn)發(fā)代理方法詳細(xì)解析(一)
10. AFNetworking源碼探究(十) —— 數(shù)據(jù)解析之?dāng)?shù)據(jù)解析架構(gòu)的分析(一)
回顧
上一篇我們主要介紹了有關(guān)數(shù)據(jù)解析類和協(xié)議棘街,以及實(shí)現(xiàn)解析的架構(gòu),這一篇就分開講述各個(gè)類是如何實(shí)現(xiàn)對(duì)應(yīng)的數(shù)據(jù)解析的承边。
AFURLResponseSerialization協(xié)議
我們先看一下這個(gè)協(xié)議的接口
/**
The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data.
For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object.
*/
@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
根據(jù)服務(wù)器響應(yīng)中的細(xì)節(jié)遭殉,AFURLResponseSerialization
協(xié)議被一個(gè)對(duì)象采用,該對(duì)象將數(shù)據(jù)解碼為更有用的對(duì)象表示博助。 Response序列化器還可以對(duì)傳入響應(yīng)和數(shù)據(jù)執(zhí)行驗(yàn)證险污。例如,JSON響應(yīng)序列化器可以檢查可接受的狀態(tài)碼(2XX
范圍)和內(nèi)容類型(application / json
)富岳,將有效的JSON響應(yīng)解碼成對(duì)象
AFHTTPResponseSerializer
這個(gè)是所有其他解析類的父類蛔糯,他遵守上面的AFURLResponseSerialization
協(xié)議。
我們看一下協(xié)議在這個(gè)類中的實(shí)現(xiàn)
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}
這里調(diào)用了一個(gè)方法窖式,進(jìn)行了指定response和數(shù)據(jù)的驗(yàn)證蚁飒。
/**
Validates the specified response and data.
In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks.
@param response The response to be validated.
@param data The data associated with the response.
@param error The error that occurred while attempting to validate the response.
@return `YES` if the response is valid, otherwise `NO`.
*/
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;
在其基本實(shí)現(xiàn)中,此方法檢查可接受的狀態(tài)碼和內(nèi)容類型萝喘。 子類可能希望添加其他域特定的檢查淮逻。
下面我們看一下驗(yàn)證過程琼懊,主要對(duì)應(yīng)下面這段代碼
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
if ([data length] > 0 && [response URL]) {
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;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
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;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}
這是一個(gè)具有返回值類型為BOOL的方法,但是這里對(duì)于返回值并沒有使用爬早。
(a) 最外層的判斷
最外層的判斷主要是
if (response && [response isKindOfClass:[NSHTTPURLResponse class]])
就是如果response不是nil哼丈,并且response的類型是NSHTTPURLResponse
。
(b) 第一個(gè)if判斷
在上面最外層判斷的內(nèi)部是兩個(gè)if判斷筛严,根據(jù)不同的條件判斷數(shù)據(jù)是否有效以及在無效時(shí)應(yīng)該拋出怎樣的異常醉旦。
主要對(duì)應(yīng)下面這段代碼
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
if ([data length] > 0 && [response URL]) {
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;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
從responseIsValid = NO
,我們可以看出來桨啃,這一定是拋出異常车胡,沒有驗(yàn)證通過的,但是為什么拋出異常呢照瘾?我們看一下匈棘。
如果有接受數(shù)據(jù)類型,如果不匹配response网杆,而且響應(yīng)類型不為空羹饰,數(shù)據(jù)長度不為0伊滋。接著進(jìn)行判斷碳却,如果數(shù)據(jù)長度大于0,而且有響應(yīng)URL笑旺,那么就生成mutableUserInfo
信息昼浦,調(diào)用下面的方法生成錯(cuò)誤信息。
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];
}
這里要注意筒主,NSURLResponse中這個(gè)MIMEType屬性关噪。
/*!
@abstract Returns the MIME type of the receiver.
@discussion The MIME type is based on the information provided
from an origin source. However, that value may be changed or
corrected by a protocol implementation if it can be determined
that the origin server or source reported the information
incorrectly or imprecisely. An attempt to guess the MIME type may
be made if the origin source did not report any such information.
@result The MIME type of the receiver.
@abstract返回接收者的MIME類型。
@討論MIME類型基于提供的信息
來源乌妙。 但是使兔,該值可能會(huì)改變或
如果可以確定原始服務(wù)器或來源報(bào)告了信息
不正確或不準(zhǔn)確,則由協(xié)議實(shí)施糾正
藤韵。如果原始資料來源未報(bào)告任何此類信息虐沥,
可以嘗試猜測MIME類型
@result接收者的MIME類型。
*/
@property (nullable, readonly, copy) NSString *MIMEType;
(c) 第二個(gè)if判斷
主要對(duì)應(yīng)下邊這段代碼
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
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;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
判斷自己可接受的狀態(tài)碼泽艘,如果和response的狀態(tài)碼不匹配欲险,則進(jìn)入if塊,生成錯(cuò)誤和標(biāo)識(shí)匹涮。
(d) error和responseIsValid判斷
主要是下面一段代碼
if (error && !responseIsValid) {
*error = validationError;
}
這里天试,如果error不為空,并且responseIsValid == NO然低,也就是說上面兩個(gè)if判斷至少走過了一個(gè)喜每,這時(shí)候給error進(jìn)行了賦值务唐。
*error = validationError;
這個(gè)方法就是來判斷返回?cái)?shù)據(jù)與咱們使用的解析器是否匹配,需要解析的狀態(tài)碼是否匹配灼卢。
兩個(gè)屬性值绍哎,一個(gè)acceptableContentTypes
,一個(gè)acceptableStatusCodes
鞋真,兩者在初始化的時(shí)候有給默認(rèn)值崇堰,如果給acceptableContentTypes
定義了不匹配的類型,那么數(shù)據(jù)仍舊會(huì)解析錯(cuò)誤涩咖。
AFJSONResponseSerializer
AFJSONResponseSerializer
是AFHTTPResponseSerializer
的一個(gè)子類海诲,用于驗(yàn)證和解碼JSON響應(yīng)。
默認(rèn)情況下檩互,AFJSONResponseSerializer
接受以下MIME類型特幔,其中包括官方標(biāo)準(zhǔn),application / json
以及其他常用類型:
application / json
text / json
text / javascript
我們看一下協(xié)議在這個(gè)類中的實(shí)現(xiàn)
- (id)responseObjectForResponse:(NSURLResponse *)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;
}
}
id responseObject = nil;
NSError *serializationError = 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
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
下面就看一下闸昨,這里都做了a什么
(a) 有效性的驗(yàn)證
我們看一下如何進(jìn)行有效性的驗(yàn)證的蚯斯。
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
就是調(diào)用我們上面解析的,驗(yàn)證有效性的方法饵较。如果無效拍嵌,進(jìn)入判斷,接著if判斷循诉,如果error為空横辆,或者有錯(cuò)誤,去函數(shù)里判斷茄猫。
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
數(shù)據(jù)無效后狈蚤,返回nil。
(b) 幾個(gè)條件判斷
下面就是幾個(gè)條件判斷划纽,滿足的話直接序列化對(duì)應(yīng)的JSON數(shù)據(jù)脆侮,不滿足的話返回nil。
id responseObject = nil;
NSError *serializationError = 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
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
- 第一組條件判斷
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
這里首先判斷數(shù)據(jù)是否為空勇劣,利用isEqualToData:
方法進(jìn)行判斷靖避,如果不為空,并且數(shù)據(jù)長度大于0芭毙,那么就進(jìn)行JSON數(shù)據(jù)的序列化筋蓖。
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
如果不滿足上面條件就返nil。
- 第二組條件判斷
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
這里有一個(gè)屬性
/**
Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`.
*/
@property (nonatomic, assign) BOOL removesKeysWithNullValues;
是否從響應(yīng)JSON中刪除具有NSNull
值的鍵退敦。 默認(rèn)為NO粘咖。如果需要移除這個(gè)鍵并且上面的responseObject已經(jīng)序列化成功,那么就要調(diào)用下面的函數(shù)移除具有NSNull
值的鍵侈百。
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
if ([JSONObject isKindOfClass:[NSArray class]]) {
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
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];
if (!value || [value isEqual:[NSNull null]]) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
}
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return JSONObject;
}
這里有一個(gè)屬性和枚舉瓮下,一起來看一下
/**
Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default.
*/
用于讀取響應(yīng)JSON數(shù)據(jù)并創(chuàng)建Foundation對(duì)象的選項(xiàng)翰铡。 有關(guān)可能的值,請(qǐng)參閱“NSJSONSerialization”文檔部分“NSJSONReadingOptions”讽坏。 默認(rèn)為'0'
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {
NSJSONReadingMutableContainers = (1UL << 0),
NSJSONReadingMutableLeaves = (1UL << 1),
NSJSONReadingAllowFragments = (1UL << 2)
} API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
- 第三組條件判斷
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
如果error不為空锭魔,那么就利用函數(shù)AFErrorWithUnderlyingError
生成NSError對(duì)象并賦值。
后記
本篇講述了一個(gè)
AFURLResponseSerialization
協(xié)議以及AFHTTPResponseSerializer
和AFJSONResponseSerializer
類中父類那個(gè)協(xié)議方法的實(shí)現(xiàn)路呜。