在AFN中,通过类AFURLResponseSerialization对服务器返回的数据进行格式化(json/xml…),下面对这个类中主要的方法进行解析。
AFURLResponseSerialization.h文件
协议:AFURLResponseSerialization
//主要是判断返回的response是否可用
- (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]]) {
// 如果response对应的mimeType不被这个ResponseSerializer所接受,那么就认为Response不可用
//acceptableContentTypes保存了 每个类能解析的内容的类型
/*
json为:@"application/json",@"text/html", @"text/json", @"text/javascript"
*/
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) {
//response不可用时候的处理
// 会返回unacceptable content-type的信息,并将错误信息记录在了mutableUserInfo中
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;
}
// 利用mutableUserInfo构建一个NSError对象
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
// 判断返回的statusCode是否被允许 如果statuscode不在acceptableStatusCodes中那么response不可以处理error
// self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
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;
}
静态方法对error进行处理
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
AFJSONResponseSerializer类
对父类的- (id)responseObjectForResponse:(NSURLResponse )response data:(NSData )data error:(NSError __autoreleasing )error进行重写
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 判断当前response是否有效
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
// 还记得validateResponse:中如果content-type不满足,那么产生的validationError就是Domain为AFURLResponseSerializationErrorDomain,code为NSURLErrorCannotDecodeContentData
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
// 因为不支持这个content-type,所以不用解析了,直接返回nil
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
// 对于'head :ok',Rails返回的是一个空格 (这是Safari上的一个bug),并且这样的JSON格式不会被NSJSONSerialization解析。
// 如果是单个空格,就不解析
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;
}
// 如果需要移除JSON数据中对应value为空(nil或NSNull)的key,那么就使用AFJSONObjectByRemovingKeysWithNullValues函数
// AFJSONObjectByRemovingKeysWithNullValues通过递归的方法,把JSON中NSDictionary的数据(不包括NSArray)中的对应value为空的key移除
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
// 如果serializationError不为空,那么最终的error其实就是serializationError
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
AFXMLParserResponseSerializer类
返回一个NSXMLParser的解析器对xml数据进行解析 e
- (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解析NSData数据
return [[NSXMLParser alloc] initWithData:data];
}
AFPropertyListResponseSerializer类
- (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;
// 使用NSPropertyListSerialization来解析NSData数据
if (data) {
responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
}
// 如果serializationError不为空,那么最终的error其实就是serializationError
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
AFImageResponseSerializer类
类型:@”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”
- (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;
}
}
// iOS和TV平台默认automaticallyInflatesResponseImage为YES
// 下面的NSData转图片的方法
if (self.automaticallyInflatesResponseImage) {
//automaticallyInflatesResponseImage设置为yes:在后台对图片进行处理,而不是在main线程中
return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale);
} else {
return AFImageWithDataAtScale(data, self.imageScale);
}
return nil;
}
获取到图片后的两种处理方式
第一种automaticallyInflatesResponseImage设置为yes:在后台对图片进行处理,而不是在main线程中
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);
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);
// 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) {
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);
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;
}
#endif
第二种:直接通过Data转为image
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];
}
static NSLock* imageLock = nil;
+ (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;
}
AFCompoundResponseSerializer类
如果返回的类型不确定就用这个方法先去确定类型找到合适的Serializer,然后再进行处理
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 可能确实不能确定返回的responsed的content-type,此时可以使用AFCompoundResponseSerializer
// 总会找到合适的Serializer
for (id <AFURLResponseSerialization> serializer in self.responseSerializers) {
if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
continue;
}
NSError *serializerError = nil;
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];
}
到此为止AFNetworking中对请求和响应的序列化操作结束,下面就是最重要的请求的方式。