版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.03.01 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請(qǐng)求,都離不開(kāi)一個(gè)非常有用的框架AFNetworking钦铺,可以說(shuō)這個(gè)框架的知名度已經(jīng)超過(guò)了蘋果的底層網(wǎng)絡(luò)請(qǐng)求部分,很多人可能不知道蘋果底層是如何發(fā)起網(wǎng)絡(luò)請(qǐng)求的,但是一定知道
AFNetworking
,接下來(lái)幾篇我們就一起詳細(xì)的解析一下這個(gè)框架误褪。感興趣的可以看上面寫的幾篇。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請(qǐng)求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
3. AFNetworking源碼探究(三) —— GET請(qǐng)求實(shí)現(xiàn)之任務(wù)進(jìn)度設(shè)置和通知監(jiān)聽(tīng)(一)
4. AFNetworking源碼探究(四) —— GET請(qǐng)求實(shí)現(xiàn)之代理轉(zhuǎn)發(fā)思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細(xì)解析(一)
6. AFNetworking源碼探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate詳細(xì)解析(一)
7. AFNetworking源碼探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate詳細(xì)解析(一)
8. AFNetworking源碼探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate詳細(xì)解析(一)
9. AFNetworking源碼探究(九) —— AFURLSessionManagerTaskDelegate中三個(gè)轉(zhuǎn)發(fā)代理方法詳細(xì)解析(一)
10. AFNetworking源碼探究(十) —— 數(shù)據(jù)解析之?dāng)?shù)據(jù)解析架構(gòu)的分析(一)
11. AFNetworking源碼探究(十一) —— 數(shù)據(jù)解析之子類中協(xié)議方法的實(shí)現(xiàn)(二)
回顧
上一篇講述了一個(gè)AFURLResponseSerialization
協(xié)議以及AFHTTPResponseSerializer
和AFJSONResponseSerializer
類中父類那個(gè)協(xié)議方法的實(shí)現(xiàn)碾褂。這一篇看一下剩下的那四個(gè)子類中該協(xié)議的實(shí)現(xiàn)兽间。
AFXMLParserResponseSerializer
我們看一下協(xié)議的實(shí)現(xiàn)
- (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];
}
這個(gè)很簡(jiǎn)單了吧,首先驗(yàn)證有效性正塌,無(wú)效的話返回nil嘀略,有效的話利用下面的方法返回對(duì)象。
[[NSXMLParser alloc] initWithData:data];
AFXMLDocumentResponseSerializer
我們看一下協(xié)議的實(shí)現(xiàn)
- (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;
}
下面我們就看一下實(shí)現(xiàn)過(guò)程:
- 首先判斷是否有效乓诽,無(wú)效的話屎鳍,return nil
- 如果有效那么就用下面方法進(jìn)行實(shí)例化對(duì)象
NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];
- 如果有錯(cuò)誤,調(diào)用方法合成error對(duì)象
AFPropertyListResponseSerializer
我們看一下協(xié)議的實(shí)現(xiàn)
- (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;
}
這個(gè)的實(shí)現(xiàn)過(guò)程和上面的是類似的问裕,唯一不同的就是驗(yàn)證有效逮壁,實(shí)例化調(diào)用的方法不一樣,這里是
responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
AFImageResponseSerializer
我們看一下協(xié)議的實(shí)現(xiàn)
- (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;
}
這里我們可以看到:
- 首先進(jìn)行的是有效性的驗(yàn)證粮宛,無(wú)效的話返回nil窥淆。
- 然后利用屬性
automaticallyInflatesResponseImage
進(jìn)行判斷邏輯的執(zhí)行。
/**
Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default.
*/
@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;
是否自動(dòng)為壓縮格式(如PNG或JPEG)膨脹響應(yīng)圖像數(shù)據(jù)巍杈。 如果與setCompletionBlockWithSuccess:failure:
一起使用忧饭,啟用它可以顯著提高iOS上的繪圖性能,因?yàn)樗试S在后臺(tái)而不是在主線程中構(gòu)建位圖表示筷畦。 默認(rèn)為YES词裤。
如果可以膨脹,調(diào)用下面函數(shù)鳖宾,返回UIImage對(duì)象并return吼砂。
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
如果不可以膨脹,那么調(diào)用下面的函數(shù)返回UIImage對(duì)象鼎文。
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];
}
AFCompoundResponseSerializer
下面看一下協(xié)議在該類中的實(shí)現(xiàn)
- (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];
}
這里要遍歷數(shù)組
/**
The component response serializers.
*/
@property (readonly, nonatomic, copy) NSArray <id<AFURLResponseSerialization>> *responseSerializers;
這個(gè)數(shù)組中的元素都是遵守AFURLResponseSerialization
協(xié)議的渔肩。然后進(jìn)行判斷如果類型不是AFHTTPResponseSerializer
,那么就continue跳出本次循環(huán)拇惋,繼續(xù)進(jìn)行下次的遍歷周偎。
如果類型是AFHTTPResponseSerializer
,就利用下面方法進(jìn)行實(shí)例化并返回撑帖。
id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
后記
本篇主要講述剩下的五個(gè)子類中協(xié)議方法的實(shí)現(xiàn)蓉坎。