美文网首页
AFNetworking源码分析之序列化

AFNetworking源码分析之序列化

作者: hallfrita | 来源:发表于2018-10-15 23:07 被阅读0次

    这个模块主要的类就是下面的这几个

    <AFURLRequestSerialization>(协议)
    AFHTTPRequestSerializer(根类)
    <AFMultipartFormData>(多部分表单,协议)
    AFJSONRequestSerializer
    AFPropertyListRequestSerializer

    <AFURLResponseSerialization>(协议)
    AFHTTPResponseSerializer(根类)
    AFJSONResponseSerializer(默认的)
    AFXMLParserResponseSerializer
    AFXMLDocumentResponseSerializer (macOS)
    AFPropertyListResponseSerializer
    AFImageResponseSerializer(重要的类)
    AFCompoundResponseSerializer

    实例

    本模块通过上传一张图片引入

    - (void)multipart{
        // 1.使用AFHTTPSessionManager的接口
        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        NSDictionary *dic = @{@"businessType":@"CC_USER_CENTER",
                              @"fileType":@"image",
                              @"file":@"img.jpeg"
                              };
        [manager POST:@"http://114.215.186.169:9002/api/demo/test/file" parameters:dic constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
            // 2.在这个block中设置需要上传的文件
            
            NSString *path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"];
            // 将本地图片数据拼接到formData中 指定name
            [formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:@"file" error:nil];
            
            // 或者使用这个接口拼接 指定name和filename
    //        NSData *picdata  =[NSData dataWithContentsOfFile:path];
    //        [formData appendPartWithFileData:picdata name:@"image" fileName:@"image.jpg" mimeType:@"image/jpeg"];
            
        } progress:^(NSProgress * _Nonnull uploadProgress) {
            NSLog(@"progress --- %@",uploadProgress.localizedDescription);
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"responseObject-------%@", responseObject);
            dispatch_async(dispatch_get_main_queue(), ^{
                
            });
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"Error-------%@", error);
        }];
        
    }
    

    POST向服务器提交数据时,一般包括三个部分:请求行、请求头、请求体。当我们要从文件、socket、NSData读取较大数据到内存时,对CPU的消耗是非常大的。所以为了防止内存爆长,AFN采用了分片上传的方式。

    对于 [AFHTTPSessionManager manager]这个还是一样,做了一个初始化。然而,[manager POST_xxx]方法却做了很多,用我们提供的数据给我们把请求三部分做了一个封装,这样就省去了我们很多的拼接。
    具体的来看

    - (NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(id)parameters
         constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                          progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
                           success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                           failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
    {
        NSError *serializationError = nil;
        NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
        if (serializationError) {
            if (failure) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
            if (error) {
                if (failure) {
                    failure(task, error);
                }
            } else {
                if (success) {
                    success(task, responseObject);
                }
            }
        }];
    
        [task resume];
    
        return task;
    }
    

    requestSerialization

    点进POST方法,我们看到,这里做了两步:生成request、用request生成task返回。用request生成的task这一步,主要是SessionManager做的,上一篇文章已经讲述,下面我们重点看一下,如何生成的request的请求体。源代码如下

    //构建一个multipartForm的request。并且通过`AFMultipartFormData`类型的formData来构建请求体
    - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                                  URLString:(NSString *)URLString
                                                 parameters:(NSDictionary *)parameters
                                  constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                      error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(method);
        //method不能是get、head
        NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
    
        NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
        // 使用initWithURLRequest:stringEncoding:来初始化一个AFStreamingMultipartFormData变量
        // 主要是为了构建bodyStream
        __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
    
        
        if (parameters) {
            for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
                NSData *data = nil;
                if ([pair.value isKindOfClass:[NSData class]]) {
                    data = pair.value;
                } else if ([pair.value isEqual:[NSNull null]]) {
                    data = [NSData data];
                } else {
                    //通常nslog打印会调用description,打印出来的是地址,但是可以重写description,来实现打印出我们想要的类型
                    data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
                }
    
                if (data) {
                    // bodyStream构造最主要的部分就在这了(虽然后面requestByFinalizingMultipartFormData函数还会稍微处理一下)
                    // 根据data和name构建Request的header和body,后面详解
                    [formData appendPartWithFormData:data name:[pair.field description]];
                }
            }
        }
    // 参考上面的例子,其实还是往formData中添加数据
        if (block) {
            block(formData);
        }
    // 做最终的处理,比如设置一下MultipartRequest的bodyStream或者其特有的content-type等等,后面也会详解
        return [formData requestByFinalizingMultipartFormData];
    }
    

    普通的post和multipart的post区别:
    1.content-type:
    2.httpbody/httpbodystrean
    requestserialization:1.动态监听我们的属性;2.设置请求头;3.生成查询字符串;4.分片上传

    responseSerialization

    对于response的序列化,我们需要注意的一个点就是,当我们下载图片时,图片是经过压缩的。所以,当我们下载图片时,需要手动解压图片。若不手动解压,这个过程就会在渲染图片时解压,这样这个解压过程就在主线程进行了,严重影响性能,还好这个过程AFN已经为我们做了。
    下面,我们看看图片解压的核心代码(AFInflatedImageFromResponseWithDataAtScale),代码有点长,

    static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
        if (!data || [data length] == 0) {
            return nil;
        }
    
        CGImageRef imageRef = NULL;
        // CoreGraphics对data的封装
        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
                // 不能支持cmyk 转为位图
                /*
                 CMKY:印刷色彩模式,用来印刷
                 RGB:
                 */
                if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
                    CGImageRelease(imageRef);
                    imageRef = NULL;
                }
            }
        }
    
        CGDataProviderRelease(dataProvider);
        //根据创建原格式图片
        UIImage *image = AFImageWithDataAtScale(data, scale);
        if (!imageRef) {
            //如果imageRef为空,说明不是压缩格式的图片,或者无法进一步转成Bitmap格式,直接返回原格式图片
            if (image.images || !image) {
                return image;
            }
            // 如果imageRef为空
            imageRef = CGImageCreateCopy([image CGImage]);
            if (!imageRef) {
                return nil;
            }
        }
    
        size_t width = CGImageGetWidth(imageRef);
        size_t height = CGImageGetHeight(imageRef);
        // 获得图片的位数
        size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
        // 大于8位或者像素大于1024*1024 如果图片太大,直接返回原格式图片
        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);
        //如果是RGB颜色模型,根据像素是否包含alpha通道进行相应处理
        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
        }
        //创建Bitmap的上下文
        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);
        //获取Bitmap格式的图片
        CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);
    
        CGContextRelease(context);
        //转化为UIImage对象
        UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];
    
        CGImageRelease(inflatedImageRef);
        CGImageRelease(imageRef);
    
        return inflatedImage;
       
    }
    

    相关文章

      网友评论

          本文标题:AFNetworking源码分析之序列化

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