AFURLResponseSerialization是用来将返回的response处理成相应的格式,它通过协议对特定response的data进行解码

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

AFHTTPResponseSerializer可以通过+ serializer- init方法进行初始化,实际上+ serializer内只是调用了- init

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

- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
// 设置字符串编码类型,可接受的状态码,可接受的MIME类型
self.stringEncoding = NSUTF8StringEncoding;
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;

return self;
}

acceptableStatusCodes和acceptableContentTypes可以通过外部进行设置

1
2
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;

然后可以调用- [validateResponse:data:error:]检查这个response是否包含可接受的状态码和可接受MIME类型来验证response的有效性,子类也可以增加特定域名检查,- [responseObjectForResponse:data:error]也是调用了这个方法,返回data

1
2
3
4
5
6
7
8
9
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 调用- [validateResponse:data:error:]方法,返回data
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

return data;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
// 设置初始值
BOOL responseIsValid = YES;
NSError *validationError = nil;

// 检查这个response是否包含可接受的状态码和可接受MIME类型

if (error && !responseIsValid) {
*error = validationError;
}
// 返回response是否有效性
return responseIsValid;
}

检查这个response是否包含可接受的状态码和可接受MIME类型

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
33
34
35
36
37
38
39
// 检查response是否为空,以及response是否是NSHTTPURLResponse类
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
// acceptableContentTypes不为空并且response的MIME类型不在可接受的范围里
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;
}
// acceptableStatusCodes不为空并且acceptableStatusCodes包含response的状态码,response的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;
}
}

但是这里有个疑问,假如response为nil或者response不是NSHTTPURLResponse类,那下面的操作均不会对responseIsValid布尔值进行修改,最后返回的是个YES,但是这样的response不应该是NO么?


AFJSONResponseSerializer是继承于AFHTTPResponseSerializer

外部可以设置NSJSONReadingOptions和是否移除空值的key

1
2
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
@property (nonatomic, assign) BOOL removesKeysWithNullValues;

转换object的时候,会检查data是否是空格,这个是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.

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
- (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;
// 判断是否是空格
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;
}
// 调用AFJSONObjectByRemovingKeysWithNullValues把空值的key都移除掉,返回object
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}

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

return responseObject;
}

AFXMLParserResponseSerializer则是直接校验response后,用data初始化NSXMLParser对象并返回

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

return [[NSXMLParser alloc] initWithData:data];
}

AFPropertyListResponseSerializer也是类似的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (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;
NSError *serializationError = nil;

if (data) {
responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
}

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

return responseObject;
}

AFImageResponseSerializer在验证response之后,会根据设置是否自动解压automaticallyInflatesResponseImage布尔值,来对imageData按图片比例返回UIImage对象

1
2
@property (nonatomic, assign) CGFloat imageScale;
@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;
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
- (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
// iOS需要手动解压图片
if (self.automaticallyInflatesResponseImage) {
return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale);
} else {
return AFImageWithDataAtScale(data, self.imageScale);
}
#else
// MacOS可以直接使用NSBitmapImageRep来解压
NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data];
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])];
[image addRepresentation:bitimage];

return image;
#endif

return nil;
}

如果不解压的话,就直接根据imageData和scale来创建Image,但是这有个疑问是,AF为什么要创建两次image,我觉得可以直接使用- [imageWithData:scale:]方法

1
2
3
4
5
6
7
8
static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
UIImage *image = [UIImage af_safeImageWithData:data];
if (image.images) {
return image;
}

return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
}

但是如果用imageWithData转成UIImage对象后,由于网络图片PNG和JPG都是压缩格式,需要解压成bitmap后才能渲染到屏幕,这时会在主线程对图片进行解压操作,这是比较耗时的,可能还会对主线程造成阻塞,所以AF还提供了AFInflatedImageFromResponseWithDataAtScale方法,对PNG和JPG解压后,返回UIImage对象,这样避免了在主线程的解压操作,不会对主线程造成卡顿

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
if (!data || [data length] == 0) {
return nil;
}
// 创建CGImageRef
CGImageRef imageRef = NULL;
// 用data创建CGDataProviderRef
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

if ([response.MIMEType isEqualToString:@"image/png"]) {
imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
} else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);

if (imageRef) {
CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef);
CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace);

// 如果色彩空间是CMKY,CGImageCreateWithJPEGDataProvider是不会进行处理的,也就是不进行解压,将调用AFImageWithDataAtScale返回image
if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
CGImageRelease(imageRef);
imageRef = NULL;
}
}
}

CGDataProviderRelease(dataProvider);
// 不符合解压条件的,将调用AFImageWithDataAtScale返回image,但是这里如果符合解压条件的也会调用,以及下面会对超出大小的,直接返回image,这里我觉得应该统一对不符合条件的返回image,符合条件的就不需要调用AFImageWithDataAtScale
UIImage *image = AFImageWithDataAtScale(data, scale);
if (!imageRef) {
if (image.images || !image) {
return image;
}
// 这里调用CGImageCreateCopy,只会对图形本身结构进行拷贝,底层的数据是不会拷贝的
imageRef = CGImageCreateCopy([image CGImage]);
if (!imageRef) {
return nil;
}
}
// 设置图片的宽和高和存储一个像素所需要用到的字节
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
// 如果图片大小宽高乘积超过1024*1024或者bitsPerComponent大于8都不解压了,因为bitmap是一直存在UIImage对象里的,可能会把内存爆了
if (width * height > 1024 * 1024 || bitsPerComponent > 8) {
CGImageRelease(imageRef);

return image;
}

// 画布参数
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
CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);

CGContextRelease(context);
// 再转成UIImage对象
UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];

CGImageRelease(inflatedImageRef);
CGImageRelease(imageRef);

return inflatedImage;
}

AFCompoundResponseSerializer

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
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 遍历responseSerializers
for (id <AFURLResponseSerialization> serializer in self.responseSerializers) {
// 如果serializer不是AFHTTPResponseSerializer类,则继续
if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
continue;
}

NSError *serializerError = nil;
// 一层一层的调用自己的- [responseObjectForResponse:data:error:],直到返回responseObject
id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
if (responseObject) {
if (error) {
*error = AFErrorWithUnderlyingError(serializerError, *error);
}

return responseObject;
}
}

return [super responseObjectForResponse:response data:data error:error];
}