流程梳理 今天開始會寫幾篇關於AFN源碼解讀的一些Blog,首先要梳理一下AFN的整體結構(主要是討論2.x版本的Session訪問模塊): 我們先看看我們最常用的一段代碼: 在前面關於 AFN URLEncode 的文章說道,AFN將網路訪問分為三個過程化的模塊,下麵我把第一部分再分為兩個步驟:
流程梳理
今天開始會寫幾篇關於AFN源碼解讀的一些Blog,首先要梳理一下AFN的整體結構(主要是討論2.x版本的Session訪問模塊):
我們先看看我們最常用的一段代碼:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:@"https://www.baidu.com" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// ... successHandler
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// ... failureHandler
}];
在前面關於 AFN URLEncode 的文章說道,AFN將網路訪問分為三個過程化的模塊,下麵我把第一部分再分為兩個步驟:
1.訪問前的準備:使用AFURLRequestSerialization類創建一個新的URLRequest對象(用於即將進行的網路訪問),對傳遞過來的URLrequest對象進行三步加工:
①配置預設網路配置,如(allowsCellularAccess,cachePolicy,HTTPShouldHandleCookies,HTTPShouldUsePipelining,networkServiceType,timeoutInterval)
②將request的HTTPHeader賦給新的request
③將parameter字典轉為queryString,拼接在URLRequest的URL後面.如果是POST,PUT,PATCH方法,則放在HTTPBody中,並設置Content-Type
頭為表單類型:application/x-www-form-urlencoded
2.用1中所得的mutableRequest對象創建dataTask
3.訪問過程中,將代理職責下放給AFURLSessionManagerTaskDelegate,通過代理方法接收數據。
4.完全接受到數據或失敗之後的處理:失敗回調、成功後解析然後回調。
上面四個步驟都是在[manager GET: parameters: success: failure:]
這個方法中完成的,而在進行網路訪問之前的[AFHTTPSessionManager manager]
是對網路訪問過程組件的初始化,也就是,在AFHTTPSessionManager
的+manager
方法中,完成了對自己和requestSerializer
以及responseSerializer
的初始化工作,+manager
方法內部的代碼:
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
可以看出requestSerializer和responseSerializer對象都是按照預設的構造方法serializer
創建的,同時可以看出responseSerializer預設使用了JSON的解析方式,著也是為什麼當使用AFN進行網路請求時,JSON會自動進行解析的原因。看到這裡我們也瞭解瞭如果想進行修改預設的request和response序列化方式修改,在何時添加這部分代碼。就是在manager的預設設置完成之後,在開始進行網路訪問三步走之前:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
[manager GET:@"https://www.baidu.com" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// ... successHandler
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// ... failureHandler
}];
我們能改變的不僅僅是request和reponse按照什麼格式序列化,還可以改變預設的session配置,進行創建Task的session對象在AFN中成為了AFHTTPSessionManager的屬性,如果不使用構造方法- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration
傳給它一個值,它會在AFHTTPSessionManager
的父類AFURLRequestSerialization
中預設配置的,不光如此,而且還配置了AFHTTPSessionManager
的很多重要屬性,在AFURLRequestSerialization
的-initWithBaseURL: sessionConfiguration:
中:
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:nil];
return self;
這些預設的配置大多是不可以在外部修改,因為大都為readonly屬性,只是在實現文件中給了修改的介面。例如:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 設置最大併發操作數
manager.operationQueue.maxConcurrentOperationCount = 3; // error
[manager GET: parameter: success: failure:];
AFN網路訪問的預設設置大多都是不希望用戶修改的,對外提供的介面也僅僅局限於request和response的序列化方式的修改。
看完了這些,我們來著重看一下網路訪問三步走的過程:
1.訪問前的準備:使用AFURLRequestSerialization類創建一個新的URLRequest對象
這一部分的很多知識點,在這篇文章 iOS. PercentEscape是錯用的URLEncode,看看AFN和Facebook吧中有介紹,這裡說一些補充的內容:
首先是requestSerializer的創建細節:這個雖然不屬於這部分內容(它是在+manager方法中就創建了),但有些問題還需註意:
這個創建過程主要是設置預設編碼為UTF8,對Accept-Language、User-Agent兩個頭的初始化,設置允許queryString放在URL中的HTTP請求方法為@"GET", @"HEAD", @"DELETE"、添加對@[@"allowsCellularAccess", @"cachePolicy", @"HTTPShouldHandleCookies", @"HTTPShouldUsePipelining", @"networkServiceType", @"timeoutInterval"]屬性值(這些key通過一個靜態數組獲得)的觀察者為本身。
需要註意的是請求頭本來是Request的屬性,這裡設置請求頭是用requestSerilizer對象的一個字典屬性mutableHTTPRequestHeaders將它們先存儲起來,以備在修改傳遞過來的request對象過程中使用。
為什麼要KVO以上6個屬性?
字典屬性mutableObservedChangedKeyPaths用來存儲這6個屬性值中非空的值,如果這6個屬性中的任何一個被賦了新值,就會在observeValueForKeyPath:中檢查新值是否為空,如果為空,就從mutableObservedChangedKeyPaths中移出這個對象,表示不再需要考慮這個值對配置的影響。
而這些非空的值會在進行網路訪問前創建新的mutableRequest對象的時候一一賦給它(這些屬性本來就是URLRequest對象的屬性)。
這個過程我們可以換一個思路實現,就是非空給屬性賦值,空時賦給屬性NSNull,在將這些屬性賦給mutableRequest的時候判斷是否為NSNull,如果是,就不賦值了。相比之下AFN的做法對擴展性更好一些。而這種方法的使用在AFN是非常常見的。
下麵我們就看一下mutableRequest創建的細節吧:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 給mutableRequest賦值剛纔在AFHTTPRequestSerializerObservedKeyPaths存儲的屬性,已經去掉了空值。
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 將HTTPRequestHeaders字典屬性中的Header傳給mutableRequest, 將格式化好的queryString傳給mutableRequest
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
剛纔的大費口舌就是剛好是對這段代碼的解釋。
準備好了request,我們就來看一下如何使用request創建dataTask
2.使用準備好的mutableRequest對象創建dataTask
在- (NSURLSessionDataTask *)dataTaskWithHTTPMethod: URLString: parameters: failure:
方法中的的後半段:
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { // 下麵會解讀這一句
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
其中的failure和success實際上是由我們使用者傳遞過來,這段非常簡單的代碼同樣是有點機關的,這其中包含了AFN設計中使用的將代理職責轉移
的思想,儘管我們平常也使用過類似的代碼,但還是研讀一下AFN如何實現的吧:
上面的dataTask的創建的核心代碼實現是這樣的:
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionDataTask *dataTask = nil;
dispatch_sync(url_session_manager_creation_queue(), ^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask completionHandler:completionHandler]; // 下麵有解析
return dataTask;
}
如上,AFN會選擇在它自定義的串列隊列url_session_manager_creation_queue
(這個隊列標記了label:"com.alamofire.networking.session.manager.creation")中採用同步的方式創建dataTask。
在dataTask被創建之後將代理職責下方給了AFURLSessionManagerTaskDelegate對象,我們可以通過查看[self addDelegateForDataTask:dataTask completionHandler:completionHandler];
得出:
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self; // AFURLSessionManagerTaskDelegate弱引用它的管理者(AFHTTPSessionManager對象)
delegate.completionHandler = completionHandler; // 將完成的回調(failure和success的處理)傳遞給AFURLSessionManagerTaskDelegate
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
}
而在的實現中:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self.lock unlock];
}
這裡AFNHTTPSessionManager將單個dataTask的代理職責下放給了一個AFURLSessionManagerTaskDelegate
對象,但是這個對象仍然受manager的控制,manager會用一個可變字典類型的屬性mutableTaskDelegatesKeyedByTaskIdentifier存儲它管理的所有的dataTask和這個dataTask對應的AFURLSessionManagerTaskDelegate
對象的關係,而具體的任務下放就是通過這種關係來實現的。
下麵就邊介紹數據請求與接收的過程階段邊解釋如何通過這種關係將代理職責下放。
3.網路訪問過程中
這一過程是由dataTask的resume方法開始的。AFHTTPSessionManager的成員session會使用上面的request進行網路請求,當接收到數據之後進入回調,AFN已將session在AFHTTPSessionManager的父類AFURLSessionManager中預設設置了self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
並且session的代理方法也已在AFURLSessionManager類中實現。而AFN在這個實現的過程中將每次接收到的數據都交給了當前dataTask對應的AFURLSessionManagerTaskDelegate
對象處理,在這裡實現了職責下放:
在AFURLSessionManager.m中:
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; // 找到dataTask對應的AFURLSessionManagerTaskDelegate對象
[delegate URLSession:session dataTask:dataTask didReceiveData:data]; // 代理職責下放
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
// 如何找到dataTask對應的AFURLSessionManagerTaskDelegate對象
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; // 根據taskId,之前以key為taskId、value為AFURLSessionManagerTaskDelegate對象的形式存入字典中。
[self.lock unlock];
return delegate;
}
而真正處理網路請求的類是AFURLSessionManagerTaskDelegate
,它從未被設置為session的delegate,而是在AFHTTPSessionManager(AFURLSessionManager)對session的代理方法的實現中主動調用。
這個數據最後被這樣處理,在AFURLSessionManagerTaskDelegate中
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
[self.mutableData appendData:data];
}
我們可以看到AFURLSessionManagerTaskDelegate類有一個mutableData屬性用來拼接接收的數據。
看一下接收完畢之後是如何處理的,先是在AFURLSessionManager中:
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
這裡先找到task對應的AFURLSessionManagerTaskDelegate對象,同樣是通過dataTask的Id,然後將處理任務交給這個delegate對象,等它處理之後,sessionManager會將這個delegate對象從字典中移除:
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[self.lock lock];
[delegate cleanUpProgressForTask:task];
[self removeNotificationObserverForTask:task];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
這樣manager管理的session進行的一次dataTask就完畢了。
再看一下在AFURLSessionManagerTaskDelegate中,如何具體處理的
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
//We no longer need the reference, so nil it out to gain back some memory.
self.mutableData = nil;
}
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
#pragma clang diagnostic pop
}
取出SessionManager,和sessionManager的responseSerializer屬性,創建userInfo字典,存放數據解析的組件對象和返回的數據等
如果有錯誤:
1.userInfo存入error,key為完成錯誤的標記,
2.創建隊列任務:在主隊列中完成回調(由最開始傳入的success和failure處理)、然後向主線程發送附帶userInfo的任務完成的通知,
3.將2創建的任務放在靜態的隊列組url_session_manager_completion_group()中執行。
沒有錯誤:
在非同步的靜態隊列url_session_manager_processing_queue(label是"com.alamofire.networking.session.manager.processing")中處理:
1.用manager的responseSerializer屬性進行數據解析,將data解析為responseObject
1.1.解析正確,將responseObject存入userInfo中,
1.2.解析失敗,將錯誤信息serializationError存入userInfo,
2.創建隊列任務:在主隊列中完成回調(由最開始傳入的success和failure處理)、然後向主線程發送附帶userInfo的任務完成的通知,
3.將2創建的任務放在靜態的隊列組url_session_manager_completion_group()中執行。
要說明的一點是:AFN只負責發送通知,而沒有對通知進行接收的處理,這部分需要使用者自己完成。
現在就只剩下數據解析的過程了還沒有介紹了。
4.數據解析
這裡主要體現的是面向對象多態
的特性。
在無論我們使用AFHTTPSessionManager對象或是使用AFURLSessionManager對象創建的dataTask在數據解析階段,都會調用上面剛剛分析完的代碼中的responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
這一句進行數據解析,而在HTTPSessionManager的manager方法中預設為我們創建了JSON類型的解析器self.responseSerializer = [AFJSONResponseSerializer serializer];
,這樣在執行過程中,就會動態地調用AFJSONResponseSerializer的-responseObjectForResponse: data: error:
方法,它的實現是這樣的:
- (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;
}
這是一個非常簡單的演算法,
1.先調用了從父類(AFHTTPResponseSerializer)集成而來的數據驗證方法,如果驗證失敗了,並且確認錯誤是由AFN解析引起的,返回nil,
2.檢驗data是否為空或者一個空格這樣的無效數據,失敗返回nil,否則將data解析為JSONObject
3.如果removesKeysWithNullValues屬性設置為YES,那麼要去掉2中的JSONObject中的value等於[NSNull null]的元素。
AFJSONResponseSerializer類是AFHTTPResponseSerializer的子類,一些初始化的設置,還有驗證數據的方法都是在AFJSONResponseSerializer中完成的。
看一下AFJSONResponseSerializer類:
- (instancetype)init {
// ...
self.stringEncoding = NSUTF8StringEncoding;
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; // 只接受statusCode為2xx
self.acceptableContentTypes = nil; // 接收的Content-Type,需要子類的init中重寫
return self;
}
- (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]]) {
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;
}
init不再多說,主要是驗證方法- (BOOL)validateResponse: data: error:
,在這個方法內部完成了這些工作:
1.設置驗證通過responseIsValid的預設值YES,錯誤validationError為nil
2.驗證
2.1對response的MIME類型驗證:如果acceptableContentTypes屬性中不包含response的MIME類型,則認為驗證失敗,responseIsValid設為NO,本地化錯誤描述,並將描述、response的URL、response對象存入userInfo字典,用這個userInfo字典創建Domain為AFURLResponseSerializationErrorDomain的NSError對象
2.2對response.statusCode驗證:如果acceptableStatusCodes屬性中不包含response.statusCode,則認為失敗,處理同2.1,
3.將錯誤賦給參數error,返回responseIsValid。
對於其他類型的解析與JSON類似,這裡列舉一下經過解析後的的id responseObject對應的類型:
manager的responseSerializer屬性類型 | 解析後的responseObject類型 |
---|---|
AFHTTPResponseSerializer | NSData |
AFJSONResponseSerializer | JSONObject(NSDictionary或NSArray) |
AFXMLParserResponseSerializer | NSXMLParser |
AFXMLDocumentResponseSerializer | NSXMLDocument |
AFPropertyListResponseSerializer | propertyList(NSDictionary或NSArray) |
AFImageResponseSerializer | iOS、TV、Watch:UIImage Mac:NSImage |
AFCompoundResponseSerializer | 用responseSerializers數組中對象依次解析, 第一個失敗,則用第二個解析,依次類推,返回第一個成功的結果 |
當獲取responseObject對象後,直接按類型使用即可,例如如果設置了manager.responseSerializer = [AFXMLParserResponseSerializer serializer],就要這樣解析:
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSXMLParser *saxParser = (NSXMLParser *)responseObject;
saxParser.delegate = self;
[saxParser parse];
}
不過若要使用相同的manager對象進行下一次網路訪問,如果不知道response的Content-Type,就要將manager的responseSerializer複原,重新設置為:
manager.responseSerializer = [AFJSONRequestSerializer serializer]; // 如果manager為AFHTTPSessionManager
manager.responseSerializer = [AFHTTPRequestSerializer serializer]; // 如果manager為AFURLSessionManager