目錄
- 前言
- 核心代碼
- AFURLResponseSerialization協(xié)議
- AFHTTPResponseSerializer
- AFJSONResponseSerializer
- AFXMLParserResponseSerializer
- AFXMLDocumentResponseSerializer
- AFPropertyListResponseSerializer
- AFImageResponseSerializer
- AFCompoundResponseSerializer
- APIDemo
- 參考資料
前言
AFNetworking源碼第五篇、大概也是最后一篇
主要看了看AFURLResponseSerialization的內(nèi)容
負責(zé)網(wǎng)絡(luò)請求成功之后服務(wù)器返回的響應(yīng)體進行格式化
代碼乍看起來也挺多洽洁、但實際上大部分都是作為AFURLResponseSerialization
子類毛仪、將不同格式(JSON/XML/PList等格式
)分別處理的重復(fù)邏輯偎肃。讀起來相對輕松很多。
但其中有很多小知識點。
AFN概述:《iOS源碼補完計劃--AFNetworking 3.1.0源碼研讀》
核心代碼
-
AFURLResponseSerialization協(xié)議
協(xié)議中包含一個解碼方法、所有的請求在結(jié)束時骇吭、都需要通過這個協(xié)議方法進行解碼
首先、我們可以先看一下AFHTTPSessionManager
以及AFURLSessionManager
中的解析器屬性歧寺。
/**
解碼器燥狰、負責(zé)響應(yīng)解碼。比如json格式等等
*/
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
/**
網(wǎng)絡(luò)請求返回的數(shù)據(jù)對象
*/
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
他們都遵循著一個協(xié)議AFURLResponseSerialization
斜筐、
就說明這個協(xié)議龙致、是解碼的關(guān)鍵。
點擊進去:
/**
AFURLResponseSerialization協(xié)議奴艾、同時需要遵循NSSecureCoding, NSCopying兩個協(xié)議
*/
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
/**
必須實現(xiàn)的方法
將響應(yīng)體進行指定方式解碼
@param response 需要處理的`NSURLResponse`
@param data 需要處理的數(shù)據(jù)
@param error 錯誤
@return 返回一個特定格式(Dic/Arr/Str/XML/Plist/圖片等)
比如AFURLSessionManager中任務(wù)結(jié)束后對data的轉(zhuǎn)碼時就這樣使用:
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
其中manager.responseSerializer為`AFJSONRequestSerializer`或者`AFPropertyListRequestSerializer`净当、均為`AFHTTPRequestSerializer(遵循AFURLResponseSerialization協(xié)議)`的子類。
*/
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
搜索這個方法蕴潦、果不其然像啼。
只在AFURLSessionManager
==>AFURLSessionManagerTaskDelegate
==>- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
、也就是請求結(jié)束的方法中被調(diào)用了:
__block id responseObject = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
也就是說潭苞、只要你主動實現(xiàn)了這個代理方法忽冻、并且將它關(guān)聯(lián)給AFURLSessionManager
或者AFHTTPSessionManager
。我們完全可以自定義出一種解析方式此疹、只要在協(xié)議中執(zhí)行即可僧诚。
這是協(xié)議代理一種很經(jīng)典的用法。也是解耦的時候代替繼承蝗碎、然后重載父類方法時通用做法湖笨。
在多人協(xié)作的時候、約定好協(xié)議然后交由其他業(yè)務(wù)實現(xiàn)蹦骑、也是提升開發(fā)效率很普遍的方式慈省。
-
AFHTTPResponseSerializer
所有
AFResponseSerializer
解析器(AFJSONResponseSerializer
/AFXMLDocumentResponseSerializer
等等)的父類。對HTTP屬性(HTTPCode
眠菇、Content-Type
)進行特化边败。
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>
//初始化
- (instancetype)init;
/**
解碼器的編碼方式
*/
@property (nonatomic, assign) NSStringEncoding stringEncoding;
/**
初始化
*/
+ (instancetype)serializer;
///-----------------------------------------
/// @name 配置響應(yīng)解碼器
///-----------------------------------------
/**
接受并解析的HTTP狀態(tài)碼。如果不為nil捎废、未包含的狀態(tài)碼將不被解析
See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
*/
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
/**
接受并解析的Content-Type笑窜。如果不為nil、未包含的Content-Type將不被解析
*/
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;
/**
檢測響應(yīng)能否被解析
@param response 響應(yīng)
@param data 二進制文件
@param error 錯誤
@return 能否被解析
*/
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;
@end
提供了編碼方式登疗、狀態(tài)碼集合排截、Content-Type集合、以及判斷能否被解析的方法一個。
@implementation AFHTTPResponseSerializer
+ (instancetype)serializer {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.stringEncoding = NSUTF8StringEncoding;
/*
NSIndexSet是一個無符號整數(shù)的集合匾寝、內(nèi)部元素具有唯一性
將200 - 299的狀態(tài)碼全部添加
等同于[indexSetM addIndexesInRange:NSMakeRange(200, 100)];
*/
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;
return self;
}
初始化搬葬、沒什么好說的荷腊。需要注意的是NSIndexSet
這個合集艳悔、是NSSet的數(shù)字版。使用的話注釋里已經(jīng)寫了女仰。
/**
檢測響應(yīng)能否被解析
@param response 響應(yīng)
@param data 二進制文件
@param error 錯誤
@return 能否被解析
*/
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
//驗證的錯誤根據(jù)
NSError *validationError = nil;
//如果response 為 NSHTTPURLResponse實例
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
//設(shè)置了acceptableContentTypes && MIMEType(Content-Type)不允許接受 && MIMEType以及data 存在
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
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`對象是不存在主錯誤的。
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
//設(shè)置了acceptableStatusCodes && statusCode(狀態(tài)碼)不允許接受 && 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`對象是不存在主錯誤的乔外。
//如果兩個判斷都出錯了、整合出來的`validationError`對象的主錯誤(error.userInfo[NSUnderlyingErrorKey])為之前content-type的error一罩。本次error信息包含在userInfo里杨幼。
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
//如果不被接受、將error傳遞
if (error && !responseIsValid) {
*error = validationError;
}
//返回是否有效
return responseIsValid;
}
這個方法可以檢測當前返回的Request能否被響應(yīng)
- 如果是
HTTPRequest
的話會根據(jù)MIMEType(Content-Type)
以及statusCode(狀態(tài)碼)
結(jié)合之前的兩個屬性集合進行判斷聂渊。 - 如果不是
HTTPRequest
差购、直接跳過返回YES。
這也能很好的解釋汉嗽。為什么沒有AFURLResponseSerializer
欲逃、而直接就是HTTPResponseSerializer
我們注意到、上面的兩個判斷都會產(chǎn)生NSError饼暑。
那么這兩個NSError如何被一個**error捕獲并且傳遞呢稳析?
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
//如果主錯誤為空、返回優(yōu)先錯誤
if (!error) {
//返回優(yōu)先錯誤
return underlyingError;
}
//如果沒有優(yōu)先錯誤 或者 主錯誤里存在優(yōu)先錯誤
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
//直接返回主錯誤
return error;
}
//重新生成錯誤信息
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
//并且將優(yōu)先錯誤弓叛、添加到錯誤信息的優(yōu)先錯誤中
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
//用主錯誤的domain彰居、code、(包含優(yōu)先錯誤的)新錯誤信息生成一個新錯誤撰筷。
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
其中用到了NSUnderlyingErrorKey
這個系統(tǒng)的key陈惰、代表優(yōu)先錯誤。
如此闭专、可以很容易的在NSError中嵌套另一個NSError奴潘。
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
//AFHTTPResponseSerializer 中這個數(shù)據(jù)轉(zhuǎn)換的協(xié)議方法并不起什么作用、直接返回了data
return data;
}
這個方法看著沒什么意義影钉、不過確實也是...唯一的作用是為了當子類沒有實現(xiàn)這個協(xié)議方法的時候画髓、程序不會崩潰。
-
AFJSONResponseSerializer
針對JSON格式特化的解析器平委、繼承
AFHTTPResponseSerializer
奈虾。
可以指定返回的對象(字典、數(shù)組、字符串肉微、是否可變)匾鸥。
還可以排空
/**
Json序列化
默認接收一下幾種類型的content-type
- `application/json`
- `text/json`
- `text/javascript`
*/
@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
//初始化
- (instancetype)init;
/**
讀取JSON文件的選項 默認 0
typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {
NSJSONReadingMutableContainers = (1UL << 0),//返回一個MDic/MArr
NSJSONReadingMutableLeaves = (1UL << 1),//返回一個MStr
NSJSONReadingAllowFragments = (1UL << 2)//允許解析最外層不是Dic或者Arr的Json、比如@"123"
} API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
*/
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
/**
是否屏蔽NSNULL碉纳、默認為NO
*/
@property (nonatomic, assign) BOOL removesKeysWithNullValues;
/**
根據(jù)指定策略創(chuàng)建一個實例
*/
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions;
@end
為JSON格式的解析特化了一些屬性
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
//默認可解析的content-type為'application/json", @"text/json", @"text/javascript'
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}
這里勿负、利用父類(AFHTTPResponseSerializer
)屬性acceptableContentTypes
指定了服務(wù)器返回的content-type
必須是JSON。
其他的子類也做了相同的操作劳曹。就不在一一指出了奴愉。
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
//檢測這個響應(yīng)是否可以被解析
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
//不能的話。
//如果error為空||
//error.code(或者error.userInfo[NSUnderlyingErrorKey]的主錯誤)=NSURLErrorCannotDecodeContentData
//(domain = AFURLResponseSerializationErrorDomain)的情況下是不能被解析的铁孵。
//總之就是上面那個是否可以被解析的判斷出錯了锭硼、并且按照正常流程搞出了error(也就是確定是contenttype或者httpcode不匹配)
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
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
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;
}
//刪除響應(yīng)里的NSNULL(內(nèi)部可以遞歸)
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
//整合error
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
實現(xiàn)的代理方法、先確定是否可以解析蜕劝、然后進行排空操作
除了注釋之外檀头、需要注意的是還有兩個函數(shù)。
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
//判斷error中的domain和code是否符合
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
//判斷優(yōu)先錯誤中domain和code是否符合
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
判斷NSError符不符合對應(yīng)code以及domain(也就是之前對contenttype或者httpcode判斷出錯時加進去的code以及domain)岖沛。用到了遞歸
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
//數(shù)組
if ([JSONObject isKindOfClass:[NSArray class]]) {
//生成一個可變數(shù)組
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
//將數(shù)組里不為NULL的元素轉(zhuǎn)譯進來
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
//如果是NSJSONReadingMutableContainers暑始、則返回一個可變的數(shù)組。否則返回一個不可變的
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
//字典
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
//遍歷所有key
for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
id value = (NSDictionary *)JSONObject[key];
if (!value || [value isEqual:[NSNull null]]) {
//value為空則移除
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
//如果value是數(shù)組或者字典烫止、遞歸排空
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
}
}
//如果是NSJSONReadingMutableContainers蒋荚、則返回一個可變的字典。否則返回一個不可變的
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
//不是數(shù)組也不是字典馆蠕、返回原對象(應(yīng)該就是個字符串了)
return JSONObject;
}
刪除對象中的NSNULL
同樣在其他子類里也會用到期升、不再一一指出
-
AFXMLParserResponseSerializer
XML解析器
@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer
@end
頭文件里并沒有什么新增的屬性
//XML解析
- (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;
}
}
//返回XML對象
return [[NSXMLParser alloc] initWithData:data];
}
XML解析的協(xié)議實現(xiàn)
-
AFXMLDocumentResponseSerializer
也是解析XML
這個子類只在mac os x上使用
@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer
- (instancetype)init;
/**
Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default.
*/
@property (nonatomic, assign) NSUInteger options;
/**
Creates and returns an XML document serializer with the specified options.
@param mask The XML document options.
*/
+ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask;
@end
- (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;
}
}
NSError *serializationError = nil;
NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return document;
}
MAC上解析XML的協(xié)議實現(xiàn)
-
AFPropertyListResponseSerializer
PList解析器
/**
PList序列化
支持以下MIME types:
- `application/x-plist`
*/
@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer
- (instancetype)init;
/**
PList 格式
typedef NS_ENUM(NSUInteger, NSPropertyListFormat) {
NSPropertyListOpenStepFormat = kCFPropertyListOpenStepFormat,
//指定屬性列表文件格式為XML格式,仍然是純文本類型互躬,不會壓縮文件
NSPropertyListXMLFormat_v1_0 = kCFPropertyListXMLFormat_v1_0,
//指定屬性列表文件格式為二進制格式播赁,文件是二進制類型,會壓縮文件
NSPropertyListBinaryFormat_v1_0 = kCFPropertyListBinaryFormat_v1_0
//指定屬性列表文件格式為ASCII碼格式吼渡,對于舊格式的屬性列表文件容为,不支持寫入操作
};
*/
@property (nonatomic, assign) NSPropertyListFormat format;
/**
PList 讀取選項
};
*/
@property (nonatomic, assign) NSPropertyListReadOptions readOptions;
/**
Creates and returns a property list serializer with a specified format, read options, and write options.
@param format The property list format.
@param readOptions The property list reading options.
*/
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
readOptions:(NSPropertyListReadOptions)readOptions;
@end
將JSON解析成PList
- (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) {
//轉(zhuǎn)化成NSPropertyListSerialization對象
responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
PList解析的協(xié)議實現(xiàn)
-
AFImageResponseSerializer
圖像格式化
/**
圖像格式化
MIME types:
- `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`
*/
@interface AFImageResponseSerializer : AFHTTPResponseSerializer
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
/**
圖片比例
*/
@property (nonatomic, assign) CGFloat imageScale;
/**
是否自動壓縮圖片(如PNG/JPEG)
當使用`setCompletionBlockWithSuccess:failure:`時、這個選項可以顯著的提高性能
默認YES
*/
@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;
#endif
@end
解析圖片的子類.可以設(shè)置圖片比例寺酪、以及是否在AFN線程解壓圖片
- (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);
}
#else
// Ensure that the image is set to it's 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;
}
這里我們發(fā)現(xiàn)有兩個函數(shù)
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];
}
直接用NSData生成UIImage
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);
//判斷響應(yīng)返回的MIMEType類型坎背,
if ([response.MIMEType isEqualToString:@"image/png"]) {
//PNG
imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
} else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
//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;
}
這個函數(shù)具體的寫法設(shè)計很多CG層面的代碼、并不了解寄雀。
但作用查到了:
這里返回的
UIImage
是含有bitmap
數(shù)據(jù)的(因為通過解壓出的bitmap
繪制出的CGImage
)
bitmap
的作用在于在將UIImage
交付給UIImageView
的時候得滤。如果沒有bitmap
將會自動解壓一次。
在這里提前解壓了盒犹、也就不會再主線程做出解壓的操作懂更。
-
AFCompoundResponseSerializer
可以包含多個解析器的復(fù)合解析器
@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer
/**
解析器數(shù)組
*/
@property (readonly, nonatomic, copy) NSArray <id<AFURLResponseSerialization>> *responseSerializers;
/**
初始化
*/
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<AFURLResponseSerialization>> *)responseSerializers;
@end
你可以使用多個實現(xiàn)了AFURLResponseSerialization
(解碼)協(xié)議的解析器來初始化它眨业。
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
//遍歷所有解析器、那個能解析就用哪個
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];
}
內(nèi)部會遍歷所有的解析器沮协、哪個能解析當前響應(yīng)體龄捡、就用哪個解析。
APIDemo
說實話兩個文件加起來兩千多行慷暂、每行都想顧及到我自己都不知道怎么寫...但是把很多代碼都進行了注釋聘殖、有興趣可以自取:
GitHub
最后
本文主要是自己的學(xué)習(xí)與總結(jié)。如果文內(nèi)存在紕漏呜呐、萬望留言斧正就斤。如果不吝賜教小弟更加感謝悍募。
參考資料
AFNetworking 3.0 源碼解讀(四)之 AFURLResponseSerialization
Plist 文件的優(yōu)化