AFNetworking源碼分析之序列化

這個模塊主要的類就是下面的這幾個

<AFURLRequestSerialization>(協(xié)議)
AFHTTPRequestSerializer(根類)
<AFMultipartFormData>(多部分表單,協(xié)議)
AFJSONRequestSerializer
AFPropertyListRequestSerializer

<AFURLResponseSerialization>(協(xié)議)
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"];
        // 將本地圖片數(shù)據(jù)拼接到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向服務器提交數(shù)據(jù)時,一般包括三個部分:請求行、請求頭、請求體。當我們要從文件、socket、NSData讀取較大數(shù)據(jù)到內(nèi)存時,對CPU的消耗是非常大的。所以為了防止內(nèi)存爆長,AFN采用了分片上傳的方式。

對于 [AFHTTPSessionManager manager]這個還是一樣,做了一個初始化。然而,[manager POST_xxx]方法卻做了很多,用我們提供的數(shù)據(jù)給我們把請求三部分做了一個封裝,這樣就省去了我們很多的拼接。
具體的來看

- (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做的,上一篇文章已經(jīng)講述,下面我們重點看一下,如何生成的request的請求體。源代碼如下

//構(gòu)建一個multipartForm的request。并且通過`AFMultipartFormData`類型的formData來構(gòu)建請求體
- (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變量
    // 主要是為了構(gòu)建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打印會調(diào)用description,打印出來的是地址,但是可以重寫description,來實現(xiàn)打印出我們想要的類型
                data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
            }

            if (data) {
                // bodyStream構(gòu)造最主要的部分就在這了(雖然后面requestByFinalizingMultipartFormData函數(shù)還會稍微處理一下)
                // 根據(jù)data和name構(gòu)建Request的header和body,后面詳解
                [formData appendPartWithFormData:data name:[pair.field description]];
            }
        }
    }
// 參考上面的例子,其實還是往formData中添加數(shù)據(jù)
    if (block) {
        block(formData);
    }
// 做最終的處理,比如設置一下MultipartRequest的bodyStream或者其特有的content-type等等,后面也會詳解
    return [formData requestByFinalizingMultipartFormData];
}

普通的post和multipart的post區(qū)別:
1.content-type:
2.httpbody/httpbodystrean
requestserialization:1.動態(tài)監(jiān)聽我們的屬性;2.設置請求頭;3.生成查詢字符串;4.分片上傳

responseSerialization

對于response的序列化,我們需要注意的一個點就是,當我們下載圖片時,圖片是經(jīng)過壓縮的。所以,當我們下載圖片時,需要手動解壓圖片。若不手動解壓,這個過程就會在渲染圖片時解壓,這樣這個解壓過程就在主線程進行了,嚴重影響性能,還好這個過程AFN已經(jīng)為我們做了。
下面,我們看看圖片解壓的核心代碼(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);
    // 根據(jù)不同的格式進行轉(zhuǎn)化
    
    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 轉(zhuǎn)為位圖
            /*
             CMKY:印刷色彩模式,用來印刷
             RGB:
             */
            if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
                CGImageRelease(imageRef);
                imageRef = NULL;
            }
        }
    }

    CGDataProviderRelease(dataProvider);
    //根據(jù)創(chuàng)建原格式圖片
    UIImage *image = AFImageWithDataAtScale(data, scale);
    if (!imageRef) {
        //如果imageRef為空,說明不是壓縮格式的圖片,或者無法進一步轉(zhuǎn)成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);
    // 獲得圖片的位數(shù)
    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顏色模型,根據(jù)像素是否包含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
    }
    //創(chuàng)建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);
    //轉(zhuǎn)化為UIImage對象
    UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];

    CGImageRelease(inflatedImageRef);
    CGImageRelease(imageRef);

    return inflatedImage;
   
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容