天天看點

AFNetworking 3.0 源碼解析之Serialization 一、AFURLRequestSerialization二、AFURLResponseSerialization

AFNetworking 3.0 源碼解析之Serialization

本部分主要的作用:網絡通信資訊序列化/反序列化

一、AFURLRequestSerialization

功能:   

負責參數轉換成NSMutableURLRequest類型,進行網絡請求。 1)建構普通請求:格式化請求參數,生成HTTP Header 2)建構multipart請求

類關系:

父類:AFHTTPRequestSerializer,二進制格式(query字元串轉換成二進制) 子類:AFJSONRequestSerializer,Json格式(Json序列化成NSData類型)           AFPropertyListRequestSerializer,Plist(一種特殊的XML,解析起來相對容易)

封裝思路:

所有類遵循一個協定AFURLRequestSerialization,協定中一個非必須實作的方法:

- (nullableNSURLRequest*)requestBySerializingRequest:(NSURLRequest*)request
                                 withParameters:(nullableid)parameters
                                    error:(NSError* _Nullable__autoreleasing *)error NS_SWIFT_NOTHROW;
           

父類AFHTTPRequestSerializer中提供外部調用接口:

- (NSMutableURLRequest*)requestWithMethod:(NSString*)method
                                 URLString:(NSString*)URLString
                                parameters:(id)parameters
                                     error:(NSError*__autoreleasing*)error
           

在這個方法中調用協定方法:

mutableRequest = [[selfrequestBySerializingRequest:mutableRequestwithParameters:parameterserror:error]mutableCopy];
           

而這個方法的實作是父類以及各個子類分别實作。是以,此處self如果是AFHTTPRequestSerializer那麼走AFHTTPRequestSerializer類下的實作,如果是 AFJSONRequestSerializer,那麼走AFJSONRequestSerializer類下的實作。然後再分别實作這個方法不同功能的實作。

下面看一下各個類不同職能分别的實作:

AFURLRequestSerialization中的實作:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [requestmutableCopy];

    [self.HTTPRequestHeadersenumerateKeysAndObjectsUsingBlock:^(id field,id value, BOOL *__unused stop) {
        if (![requestvalueForHTTPHeaderField:field]) {
            [mutableRequest setValue:valueforHTTPHeaderField:field];
        }
    }];

    NSString *query =nil;
    if (parameters) {
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                returnnil;
            }
        } else {
            switch (self.queryStringSerializationStyle) {
                caseAFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    if ([self.HTTPMethodsEncodingParametersInURIcontainsObject:[[request HTTPMethod]uppercaseString]]) { //普通GET,HEAD等,參數直接拼接在url後面用&分開
        if (query && query.length >0) {
            mutableRequest.URL = [NSURLURLWithString:[[mutableRequest.URLabsoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
        }
    } else {
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        if (![mutableRequestvalueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded"forHTTPHeaderField:@"Content-Type"];
        }
        [mutableRequest setHTTPBody:[querydataUsingEncoding:self.stringEncoding]];// 普通的POST請求參數,直接轉換成NSData設定到HTTP的body中。
    }

    return mutableRequest;
}
           

我們可以看到AFNetworking對于GET,POST請求參數的處理,一個是直接拼接在URL上面,一個是設定在HTTPBody裡面。

其中,HTTPMethodsEncodingParametersInURI的初始化如下:

self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; // 支援GET,HEAD,DELETE
           

也就是GET,HEAD,DELETE支援的是參數直接拼接URL的方式。

AFJSONRequestSerialization中的實作:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerializingRequest:request withParameters:parameters error:error];
    }

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }]; // 設定公共的請求頭

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }

        [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
    }

    return mutableRequest;
}
           

OK,跟 AFURLRequestSerialization中的實作差不多,如果是HTTPMethodsEncodingParametersInURI請求方式是GET,HEAD,DELETE,則直接調用父類的解析方法。如果是POST等其他的,那麼做了一下設定請求頭Content-Type = “application/json”,并且将paramters參數Json 序列化成NSData,設定到HTTPBody裡面。

AFPropertyListRequestSerialization中的實作:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerializingRequest:request withParameters:parameters error:error];
    }

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
        }

        [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];
    }

    return mutableRequest;
}
           

好吧,封裝思路跟 AFJSONRequestSerialization一樣,差別在于json序列化變成Plist的轉換。

二、AFURLResponseSerialization

功能:   

負責對網絡請求傳回的資料進行解析。

類關系:

父類:AFHTTPResponseSerializer,二進制格式 子類:AFJSONResponseSerializer, JSON格式           AFXMLParseResponseSerializer, XML(隻能傳回XMLParser,還需要自己通過代了解析)           AFXMLDocumentResponseSerializer, (Mac OS X)           AFPropertyListResponseSerializer,  Plist

          AFImageResponseSerializer,  Image           AFCompoundResponseSerializer, 組合

封裝思路:

跟Request的封裝思路基本相同。隻不過這個是在資料請求到之後進行的處理。遵循的是AFURLResponseSerialization協定。實作方法:

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
           

此處的傳回值是id類型的,也就是資料解析完之後的資料。

順便提一下傳回值解析的調用函數是在AFURLSessionManager中的網絡請求成功的回調中:

- (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
}
           

下面看一下各個類不同職能分别的實作:

父類AFHTTPResponseSerializer 中的實作:

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

    return data;
}
           

此處就一個方法,就是做了一個傳回的資料是否有效。但是資料是否有錯,都會傳回原始資料,沒有做任何的修改。 看一下傳回資料有效性的方法實作:

- (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;
}
           

這裡主要做了判斷是不是支援的傳回content-type類型,再就是狀态碼是不是200+,如果不滿足就不是有效的傳回資料。 看下初始化值:

self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
           

子類AFJSONResponseSerializer中的實作:

- (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;
}
           

此處看到在父類中對有效性的判斷結果并沒有做處理,而在Json轉換類中,如果傳回資料是無效的,直接就傳回nil。然後就是對傳回資料進行了Json轉換。并對結果進行了空值進行了排空。

其他的子類的封裝思路也都相似,不再一一贅述。注意的是不同的功能的子類對傳回值的類型支援是不同的。

如果文中有什麼錯誤,歡迎大家指正。

繼續閱讀