概述
上篇文章分析了AFURLRequestSerialization颖御,本篇文章主要分析一下AFURLResponseSerialization炊豪。AFURLResponseSerialization主要負(fù)責(zé)對(duì)網(wǎng)絡(luò)請(qǐng)求回來(lái)的響應(yīng)報(bào)文數(shù)據(jù)進(jìn)行反序列化迁杨。主要的類(lèi)包括AFHTTPResponseSerializer及其子類(lèi)。
AFHTTPResponseSerializer
下面是AFHTTPResponseSerializer的相關(guān)屬性:
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>
@property (nonatomic, assign) NSStringEncoding stringEncoding; //文本編碼
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes; //允許的http狀態(tài)碼
@property (nonatomic, copy, nullable) NSSet *acceptableContentTypes; //允許的content-type類(lèi)型
@end
stringEncoding屬性是文本編碼方式,用于響應(yīng)的報(bào)文數(shù)據(jù)反序列化成字符串农尖。acceptableStatusCodes是一個(gè)集合,表示客戶(hù)端可以接受的報(bào)文數(shù)據(jù)的http狀態(tài)碼良哲。acceptableContentTypes是一個(gè)集合盛卡,表示客戶(hù)端可以接受的報(bào)文數(shù)據(jù)的content-type類(lèi)型。接下來(lái)看一下初始化的代碼:
self.stringEncoding = NSUTF8StringEncoding;
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; //允許200-299
self.acceptableContentTypes = nil;
默認(rèn)指定文本編碼方式為UTF-8筑凫,acceptableStatusCodes的范圍是200-299滑沧,即HTTP響應(yīng)狀態(tài)碼是200-299并村,因?yàn)楦鶕?jù)RFC相關(guān)文檔規(guī)定,2字頭的狀態(tài)碼表示成功滓技,下面是狀態(tài)碼的分類(lèi):
消息(1字頭)哩牍、成功(2字頭)、重定向(3字頭)請(qǐng)求錯(cuò)誤(4字頭)殖属、服務(wù)器錯(cuò)誤(5姐叁、6字頭)
acceptableContentTypes為nil,允許任何content-type類(lèi)型的報(bào)文數(shù)據(jù)洗显。解析響應(yīng)報(bào)文數(shù)據(jù)的方法是:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}
該方法首先驗(yàn)證報(bào)文數(shù)據(jù)外潜,如果驗(yàn)證失敗,會(huì)生成錯(cuò)誤信息傳給error挠唆,然后直接返回報(bào)文數(shù)據(jù)处窥。下面是驗(yàn)證方法的代碼注釋?zhuān)?/p>
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) {
...//生成錯(cuò)誤信息
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
...//生成錯(cuò)誤信息
responseIsValid = NO;
}
}
...
return responseIsValid;
}
該方法首先判斷響應(yīng)的報(bào)文數(shù)據(jù)是否是NSHTTPURLResponse類(lèi)型,即HTTP的相應(yīng)報(bào)文玄组,如果是滔驾,然后判斷報(bào)文數(shù)據(jù)的content-type是否在acceptableContentTypes范圍內(nèi),如果不在俄讹,生成錯(cuò)誤信息哆致,錯(cuò)誤碼是NSURLErrorCannotDecodeContentData,即反序列化報(bào)文數(shù)據(jù)失敗患膛。然后判斷是否在acceptableStatusCodes范圍內(nèi)摊阀,如果不在,說(shuō)明HTTP請(qǐng)求異常踪蹬,生成錯(cuò)誤信息胞此,錯(cuò)誤碼是NSURLErrorBadServerResponse,即服務(wù)器返回的報(bào)文數(shù)據(jù)有問(wèn)題跃捣。
AFJSONResponseSerializer
AFJSONResponseSerializer是AFHTTPResponseSerializer的子類(lèi)漱牵,專(zhuān)門(mén)用于解析JSON格式的響應(yīng)報(bào)文數(shù)據(jù),初始化方法如下:
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}
該方法在父類(lèi)方法的基礎(chǔ)上疚漆,規(guī)定acceptableContentTypes包含"application/json"酣胀、"text/json"、"text/javascript"三種JSON格式娶聘,即報(bào)文數(shù)據(jù)的content-type只能是JSON相關(guān)的格式灵临。重寫(xiě)了父類(lèi)的解析方法,代碼注釋如下:
- (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;
@autoreleasepool { //反序列化JSON報(bào)文數(shù)據(jù)
NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding];
if (responseString && ![responseString isEqualToString:@" "]) {
data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
if (data) {
if ([data length] > 0) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
} else {
//生成錯(cuò)誤信息
}
}
}
//針對(duì)反序列化后的對(duì)象趴荸,進(jìn)行NSNull過(guò)濾
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
該方法首先驗(yàn)證響應(yīng)報(bào)文數(shù)據(jù),如果content-type不是JSON相關(guān)或者HTTP狀態(tài)碼不是成功類(lèi)型宦焦,驗(yàn)證不通過(guò)发钝,直接返回nil顿涣。然后調(diào)用NSJSONSerialization反序列化報(bào)文成OC對(duì)象responseObject。接著根據(jù)removesKeysWithNullValues屬性做NSNull類(lèi)型的過(guò)濾酝豪,層層遍歷responseObject的結(jié)構(gòu)涛碑,如果是字典屬性,且value是NSNull孵淘,則從字典中刪除相應(yīng)key/value蒲障。最后將處理后的responseObject返回。
AFXMLParserResponseSerializer
AFXMLParserResponseSerializer負(fù)責(zé)解析XML格式的報(bào)文數(shù)據(jù)瘫证,初始化方法指定了content-type是"application/xml"揉阎,"text/xml",即只支持XML格式的報(bào)文數(shù)據(jù)背捌,然后調(diào)用解析方法毙籽,代碼如下:
- (id)responseObjectForResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
...//驗(yàn)證content-type和statusCode
return [[NSXMLParser alloc] initWithData:data]; //生成XML解析器
}
該方法采用SAX的方式進(jìn)行解析,特點(diǎn)是逐行解析毡庆,需要指定delegate坑赡,調(diào)用parse方法解析,當(dāng)解析到每個(gè)節(jié)點(diǎn)的時(shí)候么抗,都會(huì)通過(guò)delegate回調(diào)給調(diào)用層毅否。這種方式讀取部分XML的時(shí)候就可以邊處理,不用等到XML加載完畢蝇刀。
AFPropertyListResponseSerializer
plist格式類(lèi)似XML格式螟加,AFPropertyListResponseSerializer負(fù)責(zé)解析plist格式的報(bào)文數(shù)據(jù),初始化方法指定了content-type是"application/x-plist"熊泵,即只支持plist格式的報(bào)文數(shù)據(jù)仰迁,然后調(diào)用解析方法,代碼如下:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
...//驗(yàn)證content-type和statusCode
id responseObject;
NSError *serializationError = nil;
if (data) {
responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
}
return responseObject;
}
該方法調(diào)用NSPropertyListSerialization的propertyListWithData:options:format:error:方法解析報(bào)文數(shù)據(jù)顽分,最后將解析后的responseObject返回徐许。
AFImageResponseSerializer
AFImageResponseSerializer是AFNetworking庫(kù)中專(zhuān)門(mén)用于解析圖片格式數(shù)據(jù)的類(lèi),負(fù)責(zé)將報(bào)文數(shù)據(jù)反序列化成UIImage對(duì)象返回卒蘸,首先初始化方法中指定了acceptableContentTypes都是image相關(guān)的格式雌隅。automaticallyInflatesResponseImage屬性用于指定是否預(yù)處理圖片數(shù)據(jù)。通常情況下網(wǎng)絡(luò)請(qǐng)求返回的報(bào)文數(shù)據(jù)data缸沃,調(diào)用[UIImage imageWithData:data]得到的圖像是PNG或者JPG等壓縮格式的恰起,如果將UIImage渲染到UIImageView上面,首先會(huì)進(jìn)行解壓趾牧,然后渲染到屏幕上检盼,這一處理在主線(xiàn)程完成。如果在子線(xiàn)程中預(yù)先解壓圖片翘单,返回Bitmap格式的UIImage對(duì)象吨枉,這樣主線(xiàn)程只要渲染圖片即可蹦渣,減輕了主線(xiàn)程的負(fù)擔(dān)。具體可以參考JSPatch大神博客中的分析貌亭。
具體負(fù)責(zé)解壓處理的方法是AFInflatedImageFromResponseWithDataAtScale柬唯,將PNG或者JPG格式的圖片轉(zhuǎn)化成位圖格式(Bitmap)的圖片返回。該方法在子線(xiàn)程中調(diào)用圃庭,代碼注釋如下:
static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
...
CGImageRef imageRef = NULL;
//根據(jù)data創(chuàng)建dataProvider
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
//判斷報(bào)文格式是否是png或者jpg
if ([response.MIMEType isEqualToString:@"image/png"]) {
imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); //創(chuàng)建png圖片
} else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); //創(chuàng)建jpg圖片
//如果存在png圖片或者jpg圖片
if (imageRef) {
//獲取圖片的顏色模型
CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef);
CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace);
//如果是CMYK模型锄奢,則無(wú)法轉(zhuǎn)成位圖,imageRef指針置為空
if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
CGImageRelease(imageRef);
imageRef = NULL;
}
}
}
CGDataProviderRelease(dataProvider);
//根據(jù)創(chuàng)建原格式圖片
UIImage *image = AFImageWithDataAtScale(data, scale);
if (!imageRef) { //如果imageRef為空剧腻,說(shuō)明不是壓縮格式的圖片拘央,或者無(wú)法進(jìn)一步轉(zhuǎn)成Bitmap格式,直接返回原格式圖片
if (image.images || !image) {
return image;
}
imageRef = CGImageCreateCopy([image CGImage]);
if (!imageRef) {
return nil;
}
}
//根據(jù)imageRef獲取圖片相關(guān)信息
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;
}
//獲取圖片相關(guān)信息
size_t bytesPerRow = 0;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
//如果是RGB顏色模型堪滨,根據(jù)像素是否包含alpha通道進(jìn)行相應(yīng)處理
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
}
//創(chuàng)建Bitmap的上下文
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
if (!context) {
CGImageRelease(imageRef);
return image;
}
//渲染到畫(huà)布上
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef);
//獲取Bitmap格式的圖片
CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
//轉(zhuǎn)化為UIImage對(duì)象
UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];
CGImageRelease(inflatedImageRef);
CGImageRelease(imageRef);
return inflatedImage;
}
該方法分為以下幾步:
首先根據(jù)報(bào)文數(shù)據(jù)創(chuàng)建圖像,如果content-type是PNG或者JPG格式蕊温,則imageRef指針指向該圖像袱箱。如果不是PNG或者JPG,或者顏色模型是CMYK义矛,則不能進(jìn)行后續(xù)轉(zhuǎn)化发笔,imageRef指針置空。
調(diào)用AFImageWithDataAtScale方法直接生成原格式的UIImage對(duì)象凉翻,如果imageRef為空了讨,說(shuō)明不能進(jìn)行后續(xù)轉(zhuǎn)化,直接返回原格式的圖片對(duì)象制轰,結(jié)束方法前计。
-
如果imageRef不為空,說(shuō)明可以進(jìn)行轉(zhuǎn)化垃杖,通過(guò)imageRef獲取圖像相關(guān)信息男杈,如下:
width Bitmap的寬度,單位為像素
height Bitmap的高度,單位為像素
bitsPerComponent 內(nèi)存中像素的每個(gè)組件的位數(shù). 例如,對(duì)于32位像素格式和RGB顏色模型调俘,你應(yīng)該將這個(gè)值設(shè)為8.
bytesPerRow Bitmap的每一行在內(nèi)存所占的比特?cái)?shù)
colorspace Bitmap上下文使用的顏色空間伶棒。
bitmapInfo Bitmap是否包含alpha通道
根據(jù)上述信息創(chuàng)建Bitmap的上下文,并渲染到畫(huà)布上彩库。
調(diào)用CGBitmapContextCreateImage方法肤无,通過(guò)上下文獲取Bitmap格式的CGImageRef對(duì)象,通過(guò)inflatedImageRef創(chuàng)建Bitmap格式的UIImage對(duì)象骇钦,返回該圖片宛渐。
結(jié)尾
本篇分析了報(bào)文數(shù)據(jù)反序列化的相關(guān)類(lèi),關(guān)于報(bào)文數(shù)據(jù)序列化和反序列化的分析到此告一段落。后續(xù)準(zhǔn)備學(xué)習(xí)和分析AFNetworking的網(wǎng)絡(luò)通信主體部分窥翩。