AFNetworking 3.0 源码阅读笔记(四)

本文介绍了AFNetworking中响应序列化的实现原理,重点分析了AFURLResponseSerialization协议的两个实现类AFHTTPResponseSerializer和AFJSONResponseSerializer,包括它们的初始化过程、验证响应有效性的方法以及如何解析JSON数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接:http://itangqi.me/2016/05/13/the-notes-of-learning-afnetworking-four/

前言

通过前面的文章,我们已经知道 AFNetworking 是对 NSURLSession 的封装,也了解它是如何发出请求的,在这里我们对发出请求以及接收响应的过程进行序列化,这涉及到两个模块

前者是处理响应的模块,将请求返回的数据解析成对应的格式。而后者的主要作用是修改请求(主要是 HTTP 请求)的头部,提供了一些语义明确的接口设置 HTTP 头部字段。

我们首先会对 AFURLResponseSerialization 进行简单的介绍,因为这个模块使用在 AFURLSessionManager 也就是核心类中,而后者 AFURLRequestSerialization 主要用于 AFHTTPSessionManager 中,因为它主要用于修改 HTTP 头部


AFURLResponseSerialization

在了解模块中类的具体实现之前,先看一下模块的结构图:

AFURLResponseSerialization 定义为协议,且协议的内容非常简单,只有一个必须实现的方法:

1
2
3
4
5
6
7
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>

- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                           data:(nullable NSData *)data
                          error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

@end

遵循该协议的类同时也要遵循 NSObject、NSSecureCoding 和 NSCopying 这三个协议,以实现 Objective-C 对象的基本行为、安全编码以及拷贝。

  1. 模块中的所有类都遵循 AFURLResponseSerialization 协议
  2. AFHTTPResponseSerializer 为模块中最终要的根类

AFHTTPResponseSerializer

下面我们对模块中最重要的根类,也就是 AFHTTPResponseSerializer 的实现进行分析。它是在 AFURLResponseSerialization 模块中最基本的类(因为 AFURLResponseSerialization 只是一个协议)

初始化

首先,依然从实例化方法入手:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+ (instancetype)serializer {
    return [[self alloc] init];
}

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.stringEncoding = NSUTF8StringEncoding;

    self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
    self.acceptableContentTypes = nil;

    return self;
}

因为是对 HTTP 响应进行序列化,所以这里设置了 stringEncodingNSUTF8StringEncoding 而且没有对接收的内容类型加以限制。

acceptableStatusCodes 设置为从 200 到 299 之间的状态码, 因为只有这些状态码表示获得了有效的响应

补充HTTP状态码

验证响应的有效性

AFHTTPResponseSerializer 中方法的实现最长,并且最重要的就是 - [AFHTTPResponseSerializer validateResponse:data:error:]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    BOOL responseIsValid = YES;
    NSError *validationError = nil;
    // 简单的为空判断和类型判断,注意如果 response 为空或类型不对,反而 responseValid 为 YES
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) {
truetruetrue#1: 返回内容类型无效
        }

        if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
truetruetrue#2: 返回状态码无效
        }
    }

    if (error && !responseIsValid) {
        *error = validationError;
    }

    return responseIsValid;
}

这个方法根据在初始化方法中初始化的属性 acceptableContentTypesacceptableStatusCodes 来判断当前响应是否有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;

其中第一、二部分的代码非常相似,出现错误时通过 AFErrorWithUnderlyingError 生成格式化之后的错误,最后设置 responseIsValid

1
2
3
4
5
6
7
8
9
10
11
12
13
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;

第二部分的代码讲解略。

协议的实现

主要看 AFURLResponseSerialization 协议的实现:

1
2
3
4
5
6
7
8
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

    return data;
}

调用上面的方法对响应进行验证,然后返回数据,并没有复杂的逻辑。

AFJSONResponseSerializer

接下来,看一下 AFJSONResponseSerializer 这个继承自 AFHTTPResponseSerializer 类的实现。

初始化方法只是在调用父类的初始化方法之后更新了 acceptableContentTypes 属性:

1
2
3
4
5
6
7
8
9
10
- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

    return self;
}
协议的实现

这个类中与父类差别最大的就是对 AFURLResponseSerialization 协议的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    #1: 验证请求

    #2: 解决一个由只包含一个空格的响应引起的 bug, 略

    #3: 序列化 JSON
true
    #4: 移除 JSON 中的 null

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

    return responseObject;
}
  1. 验证请求的有效性

    1
    2
    3
    4
    5
    
    if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
         if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
             return nil;
         }
     }
    
  2. 解决一个空格引起的 bug,见 https://github.com/rails/rails/issues/1742

  3. 序列化 JSON

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    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;
      }
    
  4. 移除 JSON 中的 null

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    trueif (self.removesKeysWithNullValues && responseObject) {
    true    responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
    true}
    true``` 
    
    其中移除 JSON 中 null 的函数 `AFJSONObjectByRemovingKeysWithNullValues` 是一个递归调用的函数:
    
    ```objectivec
    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;
    }
    

其中移除 null 靠的就是 [mutableDictionary removeObjectForKey:key] 这一行代码。

  • AFXMLParserResponseSerializerAFXMLDocumentResponseSerializerAFPropertyListResponseSerializer 、 AFImageResponseSerializer 及 AFCompoundResponseSerializer 将留给感兴趣的同学。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值