源码阅读:AFNetworking(三)——AFURLResponseSerialization

988 阅读16分钟

该文章阅读的AFNetworking的版本为3.2.0。

AFURLResponseSerialization这个类是用来解析服务器返回的数据,根据服务器返回数据的不同,解析数据的类也不同。

1.一个协议

AFURLResponseSerialization这个协议只定义了一个方法,这个方法将服务器返回的数据解析后进行返回。

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

2.七个类

这七个类又分为了一个基类和六个子类,先来看基类。

2.1 AFHTTPResponseSerializer类

这个类是其他六个类的基类。

2.1.1 接口部分

2.1.1.1 属性

/**
 字符串编码方式,默认为NSUTF8StringEncoding
 */
@property (nonatomic, assign) NSStringEncoding stringEncoding;

/**
 可接受的状态码集合,如果返回的状态码不在集合中则会验证时报错
 */
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;

/**
 可接受的Content-Type集合,如果返回的类型不在集合中则会验证时报错
 */
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;

2.1.1.2 方法

/**
 实例化方法
 */
+ (instancetype)serializer;

/**
 初始化方法
 */
- (instancetype)init;

/**
 验证服务器返回的数据
 */
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
                    data:(nullable NSData *)data
                   error:(NSError * _Nullable __autoreleasing *)error;

2.1.2 实现部分

+ (instancetype)serializer {
    // 普通的实例化方法
    return [[self alloc] init];
}

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

    // 初始化属性
    self.stringEncoding = NSUTF8StringEncoding;
    
    // 可接受的状态码为200~299之间
    self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
    self.acceptableContentTypes = nil;

    return self;
}

#pragma mark -

- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    // 设置临时变量
    BOOL responseIsValid = YES;
    NSError *validationError = nil;

    // 如果response有值,并且response是NSHTTPURLResponse类型的对象
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        // 如果设置了可接受的ContentTypes,并且响应的content-type不在可接受的范围内,并且响应的content-type有值或者返回了数据
        if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
            !([response MIMEType] == nil && [data length] == 0)) {

            // 如果返回了数据,并且响应有URL
            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;
        }

        // 如果设置了可接受状态码,并且响应的状态码不在可接受的范围内,并且响应有URL
        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;
        }
    }

    // 如果传了NSError对象,并且响应无效,就赋值后返回错误信息
    if (error && !responseIsValid) {
        *error = validationError;
    }

    return responseIsValid;
}

2.1.3 AFURLResponseSerialization协议方法的实现

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    // 作为基类,仅仅实现了对响应和数据的验证
    [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

    return data;
}

2.1.4 NSSecureCoding协议方法的实现

+ (BOOL)supportsSecureCoding {
    // 如果一个类符合 NSSecureCoding 协议并在 + supportsSecureCoding 返回 YES,就声明了它可以处理本身实例的编码解码方式,以防止替换攻击。
    return YES;
}

- (instancetype)initWithCoder:(NSCoder *)decoder {
    self = [self init];
    if (!self) {
        return nil;
    }

    self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];
    self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];
    [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))];
}

2.1.5 NSCopying协议方法的实现

- (instancetype)copyWithZone:(NSZone *)zone {
    AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
    serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone];
    serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone];

    return serializer;
}

2.2 AFJSONResponseSerializer类

通过这个类的命名可以直观的看出这个类是用来解析返回数据为JSON格式的。

2.2.1 接口部分

2.2.1.1 属性

/**
 json数据解析选项,默认是0
 */
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;

/**
 是否删除value为NSNull的key,默认是否
 */
@property (nonatomic, assign) BOOL removesKeysWithNullValues;

2.2.1.2 方法

/**
 初始化方法
 */
- (instancetype)init;

/**
 指定json解析选项的实例化工厂方法
 */
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions;

2.2.2 实现部分

+ (instancetype)serializer {
    // 重写父类方法,以不选择json解析选项的方式调用了自己的实例化工厂方法
    return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
}

+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
    // 实例化本类对象指定json解析选项并返回
    AFJSONResponseSerializer *serializer = [[self alloc] init];
    serializer.readingOptions = readingOptions;

    return serializer;
}

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

    // 设置了可接受的Content-Type
    self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

    return self;
}

2.2.3 AFURLResponseSerialization协议方法的实现

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

    // 为了解决在Safari上的一个bug,如果返回的数据为空或者是一个空格就直接返回
    // 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) {
        return nil;
    }
    
    // 解析服务器返回的二级制数据
    NSError *serializationError = nil;
    
    id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];

    // 如果没有解析后的数据就直接返回
    if (!responseObject)
    {
        // 如果传入了错误信息参数
        if (error) {
            // 就将传入的错误信息添加到serializationError的NSUnderlyingErrorKey字段中
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
        return nil;
    }
    
    // 如果想要过滤NSNull,就进行过滤
    if (self.removesKeysWithNullValues) {
        return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
    }

    return responseObject;
}

2.2.4 NSSecureCoding协议方法的实现

同2.1.4

2.2.5 NSCopying协议方法的实现

同2.1.5

2.3 AFXMLParserResponseSerializer类

当服务器返回的数据类型为XML时,用这个类进行解析,但这个类只返回NSXMLParser对象,具体的解析还要通过实现代理手动解析

2.3.1 接口部分

接口部分没有申明新的属性和方法

2.3.2 实现部分

+ (instancetype)serializer {
    // 实例化对象
    AFXMLParserResponseSerializer *serializer = [[self alloc] init];

    return serializer;
}

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

    // 设置了可接受的Content-Type
    self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil];

    return self;
}

2.3.3 AFURLResponseSerialization协议方法的实现

- (id)responseObjectForResponse:(NSHTTPURLResponse *)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;
        }
    }

    // 返回NSXMLParser对象
    return [[NSXMLParser alloc] initWithData:data];
}

2.4 AFXMLDocumentResponseSerializer类

这个类同样是用来解析当服务器返回的数据类型为XML,但是这个类用在MAC上,同样也是只返回NSXMLDocument对象,具体要手动解析

2.4.1 接口部分

2.4.1.1 属性

/**
 NSXMLDocument对象输入输出的选项,默认为0
 */
@property (nonatomic, assign) NSUInteger options;

2.4.1.2 方法

/**
 初始化方法
 */
- (instancetype)init;

/**
 实例化对象的工厂方法
 */
+ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask;

2.4.2 实现部分

+ (instancetype)serializer {
    // 调用本类的实例化工厂方法
    return [self serializerWithXMLDocumentOptions:0];
}

+ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask {
    // 实例化对象并设置参数
    AFXMLDocumentResponseSerializer *serializer = [[self alloc] init];
    serializer.options = mask;

    // 返回对象
    return serializer;
}

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

    // 设置了可接受的Content-Type
    self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil];

    return self;
}

2.4.3 AFURLResponseSerialization协议方法的实现

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

    // 实例化NSXMLDocument对象
    NSError *serializationError = nil;
    NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];

    // 如果实例化没有成功就直接返回
    if (!document)
    {
        // 如果传入了错误信息参数
        if (error) {
            // 就将传入的错误信息添加到serializationError的NSUnderlyingErrorKey字段中
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
        return nil;
    }
    
    return document;
}

2.4.4 NSSecureCoding协议方法的实现

同2.1.4

2.4.5 NSCopying协议方法的实现

同2.1.5

2.5 AFPropertyListResponseSerializer类

这个类是用来将服务器返回的数据解析为plist类型

2.5.1 接口部分

2.5.1.1 属性

/**
 指定返回的plist格式,但即使设置了也没有用,因为在实现中并没用用到这个参数而是直接设置为NULL
 */
@property (nonatomic, assign) NSPropertyListFormat format;

/**
 创建plist的可变选项
 */
@property (nonatomic, assign) NSPropertyListReadOptions readOptions;

2.5.1.2 方法

/**
 初始化方法
 */
- (instancetype)init;

/**
 实例化对象的工厂方法
 */
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
                         readOptions:(NSPropertyListReadOptions)readOptions;

2.5.2 实现部分

+ (instancetype)serializer {
    // 调用本类的实例化工厂方法
    return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0];
}

+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
                         readOptions:(NSPropertyListReadOptions)readOptions
{
    // 实例化对象并设置参数
    AFPropertyListResponseSerializer *serializer = [[self alloc] init];
    serializer.format = format;
    serializer.readOptions = readOptions;

    return serializer;
}

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

    // 设置了可接受的Content-Type
    self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil];

    return self;
}

2.5.3 AFURLResponseSerialization协议方法的实现

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

    // 如果没有数据就直接返回
    if (!data) {
        return nil;
    }
    
    // 实例化NSPropertyListSerialization对象解析data
    NSError *serializationError = nil;
    
    id responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
    
    // 如果没有解析成功就直接返回
    if (!responseObject)
    {
        if (error) {
            // 就将传入的错误信息添加到serializationError的NSUnderlyingErrorKey字段中
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
        return nil;
    }

    return responseObject;
}

2.5.4 NSSecureCoding协议方法的实现

同2.1.4

2.5.5 NSCopying协议方法的实现

同2.1.5

2.6 AFImageResponseSerializer类

这个类是用来解析服务器返回的图片数据的

2.6.1 接口部分

2.6.1.1 属性

/**
 图片的缩放因子,默认为屏幕的缩放因子
 */
@property (nonatomic, assign) CGFloat imageScale;

/**
 是否对服务器返回的图片进行自动解压,默认为自动解压
 */
@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;

2.6.2 实现部分

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

    // 设置了自身的属性
    self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil];

#if TARGET_OS_IOS || TARGET_OS_TV
    self.imageScale = [[UIScreen mainScreen] scale];
    self.automaticallyInflatesResponseImage = YES;
#elif TARGET_OS_WATCH
    self.imageScale = [[WKInterfaceDevice currentDevice] screenScale];
    self.automaticallyInflatesResponseImage = YES;
#endif

    return self;
}

2.6.3 AFURLResponseSerialization协议方法的实现

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

// 在移动端需要手动解压
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
    // 如果设置了自动解压则进行解压,否则就直接生成
    if (self.automaticallyInflatesResponseImage) {
        return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale);
    } else {
        return AFImageWithDataAtScale(data, self.imageScale);
    }
// 在MAC上可以直接调用系统方法解压
#else
    // Ensure that the image is set to it is correct pixel width and height
    NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data];
    NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])];
    [image addRepresentation:bitimage];

    return image;
// 否则就直接返回
#endif

    return nil;
}

2.6.4 NSSecureCoding协议方法的实现

同2.1.4

2.6.5 NSCopying协议方法的实现

同2.1.5

2.7 AFCompoundResponseSerializer类

这个类可以用来解析多个不同类型的数据类型

2.7.1 接口部分

2.7.1.1 属性

/**
 响应解析对象数组
 */
@property (readonly, nonatomic, copy) NSArray <id<AFURLResponseSerialization>> *responseSerializers;

2.7.1.2 方法

/**
实例化工厂方法
 */
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<AFURLResponseSerialization>> *)responseSerializers;

2.7.2 类扩展

/**
类扩展中只有一个属性用于保存传入的响应解对象数组
 */
@property (readwrite, nonatomic, copy) NSArray *responseSerializers;

2.7.3 实现部分

+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers {
    // 实例化本类对象并设置属性
    AFCompoundResponseSerializer *serializer = [[self alloc] init];
    serializer.responseSerializers = responseSerializers;

    return serializer;
}

2.7.4 AFURLResponseSerialization协议方法的实现

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    // 遍历传入的响应解析对象数组
    for (id <AFURLResponseSerialization> serializer in self.responseSerializers) {
        // 如果不是AFHTTPResponseSerializer类或者其子类就跳过进行下一个
        if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
            continue;
        }

        // 调用其对应的解析方法,解析成功则返回
        NSError *serializerError = nil;
        id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
        if (responseObject) {
            // 如果有错误就将传入的错误信息添加到serializationError的NSUnderlyingErrorKey字段中
            if (error) {
                *error = AFErrorWithUnderlyingError(serializerError, *error);
            }

            return responseObject;
        }
    }
    
    // 如果传入的响应解析对象都解析不了就调用其父类进行解析
    return [super responseObjectForResponse:response data:data error:error];
}

2.7.5 NSSecureCoding协议方法的实现

同2.1.4

2.6.6 NSCopying协议方法的实现

同2.1.5

3.三个全局静态常量

3.1 接口声明

/**
 用来识别是AFURLResponseSerialization对象解析的错误
 */
FOUNDATION_EXPORT NSString * const AFURLResponseSerializationErrorDomain;

/**
 用来识别是AFURLResponseSerialization对象解析错误中的服务器返回的NSURLResponse对象
 */
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorKey;

/**
 用来识别是AFURLResponseSerialization对象解析错误中的服务器返回的NSData对象
 */
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey;

3.2 私有实现

NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response";
NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response";
NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data";

4.五个私有静态方法

/**
 该方法用于将一个NSError对象作为另一个NSError对象的附属NSError对象,并将其放到userInfo属性NSUnderlyingErrorKey键对应的值中
 */
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
    // 没有error为空的话就直接返回underlyingError
    if (!error) {
        return underlyingError;
    }

    // 如果underlyingError为空或者error中已经有附属underlyingError就直接返回error
    if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
        return error;
    }

    // 获取error的userInfo,并将underlyingError作为键NSUnderlyingErrorKey对应的值赋给error
    NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
    mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;

    return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}

/**
 该方法用于判断error或者underlyingError是否为指定code和domain的erro
 */
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
    // 如果error符合指定条件则返回YES
    if ([error.domain isEqualToString:domain] && error.code == code) {
        return YES;
    // 如果error不符合但是有underlyingError,则递归调用本方法
    } else if (error.userInfo[NSUnderlyingErrorKey]) {
        return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
    }

    // 如果都不满足就返回NO
    return NO;
}

/**
 该方法用于过滤解析后的json数据中NSDictionary类型数据值为Null的键
 */
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
    // 如果解析后的json数据是NSArray类型的
    if ([JSONObject isKindOfClass:[NSArray class]]) {
        // 遍历元素如果还是NSArray类型的就递归调用本方法直至元素不为NSArray类型
        NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
        for (id value in (NSArray *)JSONObject) {
            [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
        }

        // 根据传入参数的可变性来返回对应可变性的NSArray对象
        return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
    // 如果解析后的json数据是NSDictionary类型的
    } else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
         // 遍历所有的key并取出对应的value
        NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
        for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
            id value = (NSDictionary *)JSONObject[key];
            // 如果value不存在或者是NSNull类型的对象,就将其移除
            if (!value || [value isEqual:[NSNull null]]) {
                [mutableDictionary removeObjectForKey:key];
            // 如果value是NSArray或者NSDictionary类型的对象就递归调用本方法直至元素不为NSArray或者NSDictionary类型的对象
            } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
                mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
            }
        }
        
        // 根据传入参数的可变性来返回对应可变性的NSDictionary对象
        return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
    }

    // 如果不是是NSArray或者NSDictionary类型的对象就直接返回
    return JSONObject;
}

/**
 生成对应缩放因子的图片
 */
static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
    // 根据二进制数据安全生成图片对象 
    UIImage *image = [UIImage af_safeImageWithData:data];
    // 如果是gif图就直接返回图片对象
    if (image.images) {
        return image;
    }
    
    // 生成对应缩放因子的图片
    return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
}

/**
 生成对应缩放因子的图片并进行解压
 */
static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
    // 如果没有数据就直接返回
    if (!data || [data length] == 0) {
        return nil;
    }

    // 创建画布和图片数据提供者
    CGImageRef imageRef = NULL;
    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

    // 如果是png格式直接解压
    if ([response.MIMEType isEqualToString:@"image/png"]) {
        imageRef = CGImageCreateWithPNGDataProvider(dataProvider,  NULL, true, kCGRenderingIntentDefault);
    // 如果是jpg格式
    } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
        // 先进行解压
        imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
        
        // 如果解压成功
        if (imageRef) {
            CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef);
            CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace);
            
            // 如果jpg的色彩空间是CMKY而不是RGB的话,不进行解压
            // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale
            if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
                CGImageRelease(imageRef);
                imageRef = NULL;
            }
        }
    }

    // 释放图片数据提供者
    CGDataProviderRelease(dataProvider);

    // 按照缩放因子生成对应的图片
    UIImage *image = AFImageWithDataAtScale(data, scale);
    // 如果没有解压成功
    if (!imageRef) {
        // 如果是gif图或者没有生成图片就直接返回
        if (image.images || !image) {
            return image;
        }
        
        // 如果没有生成图片画布就直接返回
        imageRef = CGImageCreateCopy([image CGImage]);
        if (!imageRef) {
            return nil;
        }
    }

    // 获取图片尺寸和一个像素占用的字节数
    size_t width = CGImageGetWidth(imageRef);
    size_t height = CGImageGetHeight(imageRef);
    size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);

    // 如果图片太大或者一个像素占用的字节超过8就不解压了
    if (width * height > 1024 * 1024 || bitsPerComponent > 8) {
        CGImageRelease(imageRef);

        return image;
    }

    // 配置绘图上下文的参数
    // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate
    size_t bytesPerRow = 0;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);

    if (colorSpaceModel == kCGColorSpaceModelRGB) {
        uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wassign-enum"
        if (alpha == kCGImageAlphaNone) {
            bitmapInfo &= ~kCGBitmapAlphaInfoMask;
            bitmapInfo |= kCGImageAlphaNoneSkipFirst;
        } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) {
            bitmapInfo &= ~kCGBitmapAlphaInfoMask;
            bitmapInfo |= kCGImageAlphaPremultipliedFirst;
        }
#pragma clang diagnostic pop
    }

    // 生成绘图上下文
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);

    // 释放色彩空间
    CGColorSpaceRelease(colorSpace);

    // 如果没有成功生成绘图上下文就释放画布并直接返回
    if (!context) {
        CGImageRelease(imageRef);

        return image;
    }

    // 绘制图片
    CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef);
    CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);

    // 释放绘图上下文
    CGContextRelease(context);

    // 生成图片
    UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];

    // 释放画布
    CGImageRelease(inflatedImageRef);
    CGImageRelease(imageRef);

    return inflatedImage;
}

为什么要费这么大劲解压图片呢?当我们调用UIImage的方法imageWithData:方法把数据转成UIImage对象后,其实这时UIImage对象还没准备好需要渲染到屏幕的数据,现在的网络图像PNG和JPG都是压缩格式,需要把它们解压转成bitmap后才能渲染到屏幕上,如果不做任何处理,当你把UIImage赋给UIImageView,在渲染之前底层会判断到UIImage对象未解压,没有bitmap数据,这时会在主线程对图片进行解压操作,再渲染到屏幕上。这个解压操作是比较耗时的,如果任由它在主线程做,可能会导致速度慢UI卡顿的问题。

AFImageResponseSerializer除了把返回数据解析成UIImage外,还会把图像数据解压,这个处理是在子线程(AFNetworking专用的一条线程,详见AFURLConnectionOperation),处理后上层使用返回的UIImage在主线程渲染时就不需要做解压这步操作,主线程减轻了负担,减少了UI卡顿问题。

具体实现上在AFInflatedImageFromResponseWithDataAtScale里,创建一个画布,把UIImage画在画布上,再把这个画布保存成UIImage返回给上层。只有JPG和PNG才会尝试去做解压操作,期间如果解压失败,或者遇到CMKY颜色格式的jpg,或者图像太大(解压后的bitmap太占内存,一个像素3-4字节,搞不好内存就爆掉了),就直接返回未解压的图像。

另外在代码里看到iOS才需要这样手动解压,MacOS上已经有封装好的对象NSBitmapImageRep可以做这个事。以上解释摘自这篇博客

5.一个私有分类

5.1.类扩展

/**
 利用二级制数据安全生成图片对象的方法
 */
+ (UIImage *)af_safeImageWithData:(NSData *)data;

5.2.静态对象

/**
 声明了一个锁对象用于安全生成图片
 */
static NSLock* imageLock = nil;

5.3.私有实现

+ (UIImage *)af_safeImageWithData:(NSData *)data {
    // 声明图片变量
    UIImage* image = nil;
    // 只实例化一遍锁对象
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        imageLock = [[NSLock alloc] init];
    });
    
    // 安全的利用二级制数据生成图片对象
    [imageLock lock];
    image = [UIImage imageWithData:data];
    [imageLock unlock];
    return image;
}

源码阅读系列:AFNetworking

源码阅读:AFNetworking(一)——从使用入手

源码阅读:AFNetworking(二)——AFURLRequestSerialization

源码阅读:AFNetworking(三)——AFURLResponseSerialization

源码阅读:AFNetworking(四)——AFSecurityPolicy

源码阅读:AFNetworking(五)——AFNetworkReachabilityManager

源码阅读:AFNetworking(六)——AFURLSessionManager

源码阅读:AFNetworking(七)——AFHTTPSessionManager

源码阅读:AFNetworking(八)——AFAutoPurgingImageCache

源码阅读:AFNetworking(九)——AFImageDownloader

源码阅读:AFNetworking(十)——AFNetworkActivityIndicatorManager

源码阅读:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking

源码阅读:AFNetworking(十二)——UIButton+AFNetworking

源码阅读:AFNetworking(十三)——UIImageView+AFNetworking

源码阅读:AFNetworking(十四)——UIProgressView+AFNetworking

源码阅读:AFNetworking(十五)——UIRefreshControl+AFNetworking

源码阅读:AFNetworking(十六)——UIWebView+AFNetworking