美文网首页AFNetworking源码探究
AFNetworking源码探究(十二) —— 数据解析之子类中

AFNetworking源码探究(十二) —— 数据解析之子类中

作者: 刀客传奇 | 来源:发表于2018-03-01 20:32 被阅读35次

    版本记录

    版本号 时间
    V1.0 2018.03.01

    前言

    我们做APP发起网络请求,都离不开一个非常有用的框架AFNetworking,可以说这个框架的知名度已经超过了苹果的底层网络请求部分,很多人可能不知道苹果底层是如何发起网络请求的,但是一定知道AFNetworking,接下来几篇我们就一起详细的解析一下这个框架。感兴趣的可以看上面写的几篇。
    1. AFNetworking源码探究(一) —— 基本介绍
    2. AFNetworking源码探究(二) —— GET请求实现之NSURLSessionDataTask实例化(一)
    3. AFNetworking源码探究(三) —— GET请求实现之任务进度设置和通知监听(一)
    4. AFNetworking源码探究(四) —— GET请求实现之代理转发思想(一)
    5. AFNetworking源码探究(五) —— AFURLSessionManager中NSURLSessionDelegate详细解析(一)
    6. AFNetworking源码探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate详细解析(一)
    7. AFNetworking源码探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate详细解析(一)
    8. AFNetworking源码探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate详细解析(一)
    9. AFNetworking源码探究(九) —— AFURLSessionManagerTaskDelegate中三个转发代理方法详细解析(一)
    10. AFNetworking源码探究(十) —— 数据解析之数据解析架构的分析(一)
    11. AFNetworking源码探究(十一) —— 数据解析之子类中协议方法的实现(二)

    回顾

    上一篇讲述了一个AFURLResponseSerialization协议以及AFHTTPResponseSerializerAFJSONResponseSerializer类中父类那个协议方法的实现。这一篇看一下剩下的那四个子类中该协议的实现。


    AFXMLParserResponseSerializer

    我们看一下协议的实现

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

    这个很简单了吧,首先验证有效性,无效的话返回nil,有效的话利用下面的方法返回对象。

    [[NSXMLParser alloc] initWithData:data];
    

    AFXMLDocumentResponseSerializer

    我们看一下协议的实现

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

    下面我们就看一下实现过程:

    • 首先判断是否有效,无效的话,return nil
    • 如果有效那么就用下面方法进行实例化对象
    NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError];
    
    • 如果有错误,调用方法合成error对象

    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;
    
        if (data) {
            responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
        }
    
        if (error) {
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
    
        return responseObject;
    }
    

    这个的实现过程和上面的是类似的,唯一不同的就是验证有效,实例化调用的方法不一样,这里是

    responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
    

    AFImageResponseSerializer

    我们看一下协议的实现

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

    这里我们可以看到:

    • 首先进行的是有效性的验证,无效的话返回nil。
    • 然后利用属性automaticallyInflatesResponseImage进行判断逻辑的执行。
    /**
     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;
    

    是否自动为压缩格式(如PNG或JPEG)膨胀响应图像数据。 如果与setCompletionBlockWithSuccess:failure:一起使用,启用它可以显着提高iOS上的绘图性能,因为它允许在后台而不是在主线程中构建位图表示。 默认为YES。

    如果可以膨胀,调用下面函数,返回UIImage对象并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
    

    如果不可以膨胀,那么调用下面的函数返回UIImage对象。

    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

    下面看一下协议在该类中的实现

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

    这里要遍历数组

    /**
     The component response serializers.
     */
    @property (readonly, nonatomic, copy) NSArray <id<AFURLResponseSerialization>> *responseSerializers;
    

    这个数组中的元素都是遵守AFURLResponseSerialization协议的。然后进行判断如果类型不是AFHTTPResponseSerializer,那么就continue跳出本次循环,继续进行下次的遍历。

    如果类型是AFHTTPResponseSerializer,就利用下面方法进行实例化并返回。

    id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
    

    后记

    本篇主要讲述剩下的五个子类中协议方法的实现。

    相关文章

      网友评论

        本文标题:AFNetworking源码探究(十二) —— 数据解析之子类中

        本文链接:https://www.haomeiwen.com/subject/heenxftx.html