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