通讀AFN①--從建立manager到數據解析完畢

流程梳理

今天開始會寫幾篇關於AFN源碼解讀的一些Blog,首先要梳理一下AFN的總體結構(主要是討論2.x版本的Session訪問模塊):
咱們先看看咱們最經常使用的一段代碼:html

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將網絡訪問分爲三個過程化的模塊,下面我把第一部分再分爲兩個步驟:git

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-urlencodedgithub

2.用1中所得的mutableRequest對象建立dataTask算法

3.訪問過程當中,將代理職責下放給AFURLSessionManagerTaskDelegate,經過代理方法接收數據。數組

4.徹底接受到數據或失敗以後的處理:失敗回調、成功後解析而後回調。網絡

上面四個步驟都是在[manager GET: parameters: success: failure:]這個方法中完成的,而在進行網絡訪問以前的[AFHTTPSessionManager manager]是對網絡訪問過程組件的初始化,也就是,在AFHTTPSessionManager+manager方法中,完成了對本身和requestSerializer以及responseSerializer的初始化工做,+manager方法內部的代碼:session

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:中:app

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
相關文章
相關標籤/搜索