美文网首页
2018-08-06

2018-08-06

作者: Xcode_破晓 | 来源:发表于2018-08-06 11:22 被阅读0次

     #AFNetworking源码阅读系列

    图解各个模块

    一 前言:

    AFNetWorking一款轻量级网络请求开源框架,基于iOS和mac os 网络进行扩展的高性能框架,大大降低了iOS开发工程师处理网络请求的难度,让iOS开发变成一件愉快的事情。

       GitHub地址:https://github.com/AFNetworking/AFNetworking

    二 模块 :

    AFNetWorking分为六大模块

    1 :AFURLSessionManager 首先浏览完这个类从API,发现其主要提供了数据的请求、上传和下载功能

    在属性方面:tasks, dataTasks,uploadTasks,downloadTasks

    通过这四个属性,我们分别可以拿到总的任务集合、数据任务集合、上传任务集合和下载任务集合

    attemptsToRecreateUploadTasksForBackgroundSessions 在创建失败的时候,会重新尝试创建,次数默认为3次

    实现文件:

    网络请求是delegate返回结果

    -(NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request

                                   uploadProgress:(nullable void(^)(NSProgress *uploadProgress))uploadProgressBlock

                                 downloadProgress:(nullable void(^)(NSProgress *downloadProgress))downloadProgressBlock

                                completionHandler:(nullable void(^)(NSURLResponse *response,id _Nullable responseObject,  NSError * _Nullable error))completionHandler {

    __block NSURLSessionDataTask *dataTask = nil;

        url_session_manager_create_task_safely(^{

            dataTask =[self.session dataTaskWithRequest:request];

        });

    //每个task里面都会调用addDelegate方法

        [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

        return dataTask;

    }

    -(void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask

                    uploadProgress:(nullable void(^)(NSProgress *uploadProgress))uploadProgressBlock

                  downloadProgress:(nullable void(^)(NSProgress *downloadProgress))downloadProgressBlock

                 completionHandler:(void(^)(NSURLResponse *response,id responseObject,NSError *error))completionHandler

    {

        //初始化delegate对象

        AFURLSessionManagerTaskDelegate *delegate =[[AFURLSessionManagerTaskDelegate alloc]init];

        delegate.manager = self;

        //将task的completionHandler赋给delegate,系统网络请求delegate调用该block,返回结果

        delegate.completionHandler = completionHandler;

        dataTask.taskDescription = self.taskDescriptionForSessionTasks;

        //对task进行delegate

        [self setDelegate:delegate forTask:dataTask];

    //设置上传和下载进度回调

        delegate.uploadProgressBlock = uploadProgressBlock;

        delegate.downloadProgressBlock = downloadProgressBlock;

    }

    delegate对象利用kvo将task对一些方法进行监听

    // task使用kvo对一些方法监听,返回上传或者下载的进度

        [delegate setupProgressForTask:task];

        // sessionManager对暂停task和恢复task进行注册通知

        [self addNotificationObserverForTask:task];

    // task对接收到的字节数、期望接收到的字节数、发送的字节数、期望发送的字节数设置监听

        [task addObserver:self

               forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))

                  options:NSKeyValueObservingOptionNew

                  context:NULL];

    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

        if([object isKindOfClass:[NSURLSessionTask class]]||[object isKindOfClass:[NSURLSessionDownloadTask class]]){

            //设置上传和下载的新值

        }

        else if([object isEqual:self.downloadProgress]){

            if(self.downloadProgressBlock){

                self.downloadProgressBlock(object);

            }

        }

        else if([object isEqual:self.uploadProgress]){

            if(self.uploadProgressBlock){

                self.uploadProgressBlock(object);

            }

        }

    }

    在NSURLSessionTaskDelegate的代理里面,只是做了两件事情,第一个是获取数据,将responseSerializer和downloadFileURL或data存到userInfo里面,第二个是根据error是否为空值,做下一步处理

    #pragma mark - NSURLSessionTaskDelegate

    -(void)URLSession:(__unused NSURLSession *)session

                  task:(NSURLSessionTask *)task

    didCompleteWithError:(NSError *)error

    {

    #pragma clang diagnostic push

    #pragma clang diagnostic ignored "-Wgnu"

        //获取数据,将responseSerializer和downloadFileURL或data存到userInfo里面

        __strong AFURLSessionManager *manager = self.manager;

        __block id responseObject = nil;

        __block NSMutableDictionary *userInfo =[NSMutableDictionary dictionary];

        userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey]= manager.responseSerializer;

        //Performance Improvement from #2672

        NSData *data = nil;

        if(self.mutableData){

            data =[self.mutableData copy];

            //We no longer need the reference,so nil it out to gain back some memory.

            self.mutableData = nil;

        }

        if(self.downloadFileURL){

            userInfo[AFNetworkingTaskDidCompleteAssetPathKey]= self.downloadFileURL;

        } else if(data){

            userInfo[AFNetworkingTaskDidCompleteResponseDataKey]= data;

        }

        if(error){

          //有error时处理

        } else {

          //无error时正常处理

        }

    #pragma clang diagnostic pop

    }

    2:AFHTTPSessionManager

    AFHTTPSessionManager继承于AFURLSessionManager,提供了更方便的HTTP请求方法,包括了GET、POST、PUT、PATCH、DELETE这五种方式,并且AF鼓励我们在AFHTTPSessionManager再进行一次封装来满足我们自己的业务需求

    baseURL 在初始化的方法里面,我们看到这个方法

    -(instancetype)initWithBaseURL:(nullable NSURL *)url;

    -(instancetype)initWithBaseURL:(nullable NSURL *)url

               sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

    NS_DESIGNATED_INITIALIZER的作用是什么呢?

    指定的构造器通过发送初始化消息到父类来保证object被完全初始化

    -(nullable NSURLSessionDataTask *)GET:(NSString *)URLString

                       parameters:(nullable id)parameters

                          success:(nullable void(^)(NSURLSessionDataTask *task,id _Nullable responseObject))success

                          failure:(nullable void(^)(NSURLSessionDataTask * _Nullable task,NSError *error))failure DEPRECATED_ATTRIBUTE;

    下面POST、GET、PUT、PATCH、DELETE方法传参基本都是大同小异

    URLString表示请求的URL,parameters表示客户端请求内容的存储器,progress表示请求的进度,constructingBodyWithBlock里面只有一个formData用来拼接到HTTP的请求体,success表示请求成功后的block回调,failure表示请求失败的block回调

    -(NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method

                                           URLString:(NSString *)URLString

                                          parameters:(id)parameters

                                      uploadProgress:(nullable void(^)(NSProgress *uploadProgress))uploadProgress

                                    downloadProgress:(nullable void(^)(NSProgress *downloadProgress))downloadProgress

                                             success:(void(^)(NSURLSessionDataTask *,id))success

                                             failure:(void(^)(NSURLSessionDataTask *,NSError *))failure

    {

        NSError *serializationError = nil;

        NSMutableURLRequest *request =[self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL]absoluteString]parameters:parameters 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 *dataTask = nil;

        dataTask =[self dataTaskWithRequest:request

                              uploadProgress:uploadProgress

                            downloadProgress:downloadProgress

                           completionHandler:^(NSURLResponse * __unused response,id responseObject,NSError *error){

            //失败成功处理

        }];

        return dataTask;

    }

    3:NSAppTransportSecurity

    AFSecurityPolicy主要的作用就是验证HTTPS请求的证书的有效性

    AFSecurityPolicy是安全策略类,有三种SSL Pinning模式

    typedef NS_ENUM(NSUInteger,AFSSLPinningMode){

        AFSSLPinningModeNone,//在证书列表中校验服务端返回的证书

        AFSSLPinningModePublicKey,//客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥

        AFSSLPinningModeCertificate,// 客户端要有服务端的证书拷贝,第一步先验证证书域名/有效期等信息,第二步对服务端返回的证书和客户端返回的是否一致

    };

    这个是证书集合,泛型里面表示了集合里面是NSData类型,表明这个是用来存证书数据的集合,这些证书根据SSL Pinning模式来和服务器进行校验,默认是没有证书的,我们需要调用+ certificatesInBundle:方法将bundle里面的证书文件转成里面是data类型的集合

    第三种是需要我们多传入一个证书集合

    +(instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {

        AFSecurityPolicy *securityPolicy =[[self alloc]init];

        securityPolicy.SSLPinningMode = pinningMode;

        [securityPolicy setPinnedCertificates:pinnedCertificates];

        return securityPolicy;

    }

    设置证书的时候,就是把上面初始化时传入的证书取出公钥,再把公钥保存到mutablePinnedPublicKeys集合中

    -(void)setPinnedCertificates:(NSSet *)pinnedCertificates {

        _pinnedCertificates = pinnedCertificates;

        if(self.pinnedCertificates){

            NSMutableSet *mutablePinnedPublicKeys =[NSMutableSet setWithCapacity:[self.pinnedCertificates count]];

            for(NSData *certificate in self.pinnedCertificates){

                //取出公钥

    id publicKey = AFPublicKeyForCertificate(certificate);

                if(!publicKey){

                    continue;

                }

                //将公钥存到集合

                [mutablePinnedPublicKeys addObject:publicKey];

            }

            self.pinnedPublicKeys =[NSSet setWithSet:mutablePinnedPublicKeys];

        } else {

            self.pinnedPublicKeys = nil;

        }

    }

    在AFPublicKeyForCertificate方法里面,做了一系列操作后返回公钥,如下:

    -(void)setPinnedCertificates:(NSSet *)pinnedCertificates {

        _pinnedCertificates = pinnedCertificates;

        if(self.pinnedCertificates){

            NSMutableSet *mutablePinnedPublicKeys =[NSMutableSet setWithCapacity:[self.pinnedCertificates count]];

            for(NSData *certificate in self.pinnedCertificates){

                //取出公钥

    id publicKey = AFPublicKeyForCertificate(certificate);

                if(!publicKey){

                    continue;

                }

                //将公钥存到集合

                [mutablePinnedPublicKeys addObject:publicKey];

            }

            self.pinnedPublicKeys =[NSSet setWithSet:mutablePinnedPublicKeys];

        } else {

            self.pinnedPublicKeys = nil;

        }

    }

    4:AFNetworkReachabilityManager

    是用来监测网络状态的类,可以通过设置状态改变回调来获得当前网络状态

    网络的状态值有以下四种

    typedef NS_ENUM(NSInteger,AFNetworkReachabilityStatus){

        AFNetworkReachabilityStatusUnknown          = -1,//未知

        AFNetworkReachabilityStatusNotReachable    = 0,//不可用

        AFNetworkReachabilityStatusReachableViaWWAN = 1,//无线广域网连接

        AFNetworkReachabilityStatusReachableViaWiFi = 2,// WiFi连接

    };

    AFNetworkReachabilityManager提供了五种初始化的方法  

    如:+(instancetype)sharedManager {

        static AFNetworkReachabilityManager *_sharedManager = nil;

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken,^{

            //调用+ manager初始化方法

            _sharedManager =[self manager];

        });

        return _sharedManager;

    }

    开启监听 

    -(void)startMonitoring {

        //停止监听

        [self stopMonitoring];

        if(!self.networkReachability){

            return;

        }

        //收到callback调用后,将status通过networkReachabilityStatusBlock回调出去

        __weak __typeof(self)weakSelf = self;

        AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status){

            __strong __typeof(weakSelf)strongSelf = weakSelf;

            strongSelf.networkReachabilityStatus = status;

            if(strongSelf.networkReachabilityStatusBlock){

                strongSelf.networkReachabilityStatusBlock(status);

            }

        };

    //声明SCNetworkReachabilityContext结构体

        SCNetworkReachabilityContext context = {0,(__bridge void *)callback,AFNetworkReachabilityRetainCallback,AFNetworkReachabilityReleaseCallback,NULL};

        //设置回调

    SCNetworkReachabilitySetCallback(self.networkReachability,AFNetworkReachabilityCallback,&context);

        //加到Main runloop里面对其进行监测

    SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability,CFRunLoopGetMain(),kCFRunLoopCommonModes);

    //获取当前的网络状态,调用callback

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0),^{

            SCNetworkReachabilityFlags flags;

            if(SCNetworkReachabilityGetFlags(self.networkReachability,&flags)){

                AFPostReachabilityStatusChange(flags,callback);

            }

        });

    }

    如果没有设置回调的话,也可以通过注册通知的方式,收到网络状态的变化 

    static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags,AFNetworkReachabilityStatusBlock block){

        //获取当前的status

    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);

        dispatch_async(dispatch_get_main_queue(),^{

            //返回status值

    if(block){

                block(status);

            }

    //同时会发送一个通知

            NSNotificationCenter *notificationCenter =[NSNotificationCenter defaultCenter];

            NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status)};

            [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];

        });

    }

    5:AFURLRequestSerialization 

    是用来对发出的请求进行一些处理

    AFPercentEscapedStringFromString方法将string里面的:#[]@!$&’()*+,;=字符替换成% 

    NSString * AFPercentEscapedStringFromString(NSString *string){

    static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@";// does not include "?" or "/" due to RFC 3986 - Section 3.4

        static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";

    //从可用字符替换删除掉:#[]@!$&'()*+,;=这些字符

        NSMutableCharacterSet * allowedCharacterSet =[[NSCharacterSet URLQueryAllowedCharacterSet]mutableCopy];

        [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];

    // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028

        // return[string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];

    //声明批量处理的大小为50

        static NSUInteger const batchSize = 50;

        NSUInteger index = 0;

        NSMutableString *escaped = @"".mutableCopy;

    //循环将string里面:#[]@!$&'()*+,;=的字符替换成%

        while(index < string.length){

    #pragma GCC diagnostic push

    #pragma GCC diagnostic ignored "-Wgnu"

            NSUInteger length = MIN(string.length - index,batchSize);

    #pragma GCC diagnostic pop

            NSRange range = NSMakeRange(index,length);

            // To avoid breaking up character sequences such as👴🏻👮🏽

            range =[string rangeOfComposedCharacterSequencesForRange:range];

            NSString *substring =[string substringWithRange:range];

            NSString *encoded =[substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];

            [escaped appendString:encoded];

            index += range.length;

        }

    return escaped;

    }

    在AFQueryStringPair类里面有个- URLEncodedStringValue方法,将请求里面的URL参数转成field=value形式 

    -(NSString *)URLEncodedStringValue {

        if(!self.value ||[self.value isEqual:[NSNull null]]){

            return AFPercentEscapedStringFromString([self.field description]);

        } else {

            return[NSString stringWithFormat:@"%@=%@",AFPercentEscapedStringFromString([self.field description]),AFPercentEscapedStringFromString([self.value description])];

        }

    }

    字典里面是我们查询的key和value,我们通过将字典内容转成AFQueryStringPair对象,调用- URLEncodedStringValue方法,转成key=value,放到mutablePairs数组里,最后用&符拼接起来 

    NSString * AFQueryStringFromParameters(NSDictionary *parameters){

        NSMutableArray *mutablePairs =[NSMutableArray array];

        for(AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)){

            [mutablePairs addObject:[pair URLEncodedStringValue]];

        }

        return[mutablePairs componentsJoinedByString:@"&"];

    }

    NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key,id value){

        NSMutableArray *mutableQueryStringComponents =[NSMutableArray array];

        NSSortDescriptor *sortDescriptor =[NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];

    //如果是字典,遍历后返回key[nestedKey]=nestedValue

        if([value isKindOfClass:[NSDictionary class]]){

            NSDictionary *dictionary = value;

            // Sort dictionary keys to ensure consistent ordering in query string,which is important when deserializing potentially ambiguous sequences,such as an array of dictionaries

            for(id nestedKey in[dictionary.allKeys sortedArrayUsingDescriptors:@[sortDescriptor]]){

                id nestedValue = dictionary[nestedKey];

                if(nestedValue){

                    [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ?[NSString stringWithFormat:@"%@[%@]",key,nestedKey]: nestedKey),nestedValue)];

                }

            }

        } //如果是数组,遍历后返回key[]=nestedValue

    else if([value isKindOfClass:[NSArray class]]){

            NSArray *array = value;

            for(id nestedValue in array){

                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]",key],nestedValue)];

            }

        } //如果是集合,遍历后返回key=obj

    else if([value isKindOfClass:[NSSet class]]){

            NSSet *set = value;

            for(id obj in[set sortedArrayUsingDescriptors:@[sortDescriptor]]){

                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key,obj)];

            }

        } //其他返回key=value 

    else {

            [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc]initWithField:key value:value]];

        }

        return mutableQueryStringComponents;

    }

    我们使用AFHTTPRequestSerializer对HTTP请求的头部进行处理

    首先调用+ serializer进行初始化,里面调用了自己init方法

    init里面先将Accept-Language存到mutableHTTPRequestHeaders里

    然后拼接User-Agent,格式为”%@/%@ (%@; iOS %@; Scale/%0.2f)”,里面需要5个参数,第一个参数先获取项目名,如果没有,就用BundleIdentifier,第二个参数先获取短版本号,如果没有就用版本号,第三个参数是当前设备的类型,第四个参数是当前设备的版本号,第五个参数是屏幕的比例 

    然后设置属性的监听,这些属性在头文件里面都可以找到,实现文件里面也实现了set方法 

    通过KVO判断是否是新值,如果是的话,就加到mutableObservedChangedKeyPaths里面 

    设置验证字段 

    初始化之后,需要调用 

    -(NSMutableURLRequest *)requestWithMethod:(NSString *)method

                                     URLString:(NSString *)URLString

                                    parameters:(id)parameters

                                         error:(NSError *__autoreleasing *)error

    {

        //断言                                     

        NSParameterAssert(method);

        NSParameterAssert(URLString);

        NSURL *url =[NSURL URLWithString:URLString];

        NSParameterAssert(url);

    //根据url初始化request

        NSMutableURLRequest *mutableRequest =[[NSMutableURLRequest alloc]initWithURL:url];

        //设置HTTP方法                                   

        mutableRequest.HTTPMethod = method;

    //根据mutableObservedChangedKeyPaths存储的属性,设置到mutableRequest

        for(NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()){

            if([self.mutableObservedChangedKeyPaths containsObject:keyPath]){

                [mutableRequest setValue:[self valueForKeyPath:keyPath]forKey:keyPath];

            }

        }

    //调用-[requestBySerializingRequest:withParameters:error]方法

        mutableRequest =[[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error]mutableCopy];

    return mutableRequest;

    }

    6:AFURLResponseSerialization

    是用来将返回的response处理成相应的格式,它通过协议对特定response的data进行解码

    -(nullable id)responseObjectForResponse:(nullable NSURLResponse *)response

                               data:(nullable NSData *)data

                              error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

    acceptableStatusCodes和acceptableContentTypes可以通过外部进行设置

    @property(nonatomic,copy,nullable)NSIndexSet *acceptableStatusCodes;

    @property(nonatomic,copy,nullable)NSSet *acceptableContentTypes;

    然后可以调用- [validateResponse:data:error:]检查这个response是否包含可接受的状态码和可接受MIME类型来验证response的有效性,子类也可以增加特定域名检查,- [responseObjectForResponse:data:error]也是调用了这个方法,返回data 

    -(id)responseObjectForResponse:(NSURLResponse *)response

                               data:(NSData *)data

                              error:(NSError *__autoreleasing *)error

    {

        //调用-[validateResponse:data:error:]方法,返回data 

        [self validateResponse:(NSHTTPURLResponse *)response data:data error:error];

        return data;

    }

    -(BOOL)validateResponse:(NSHTTPURLResponse *)response

                        data:(NSData *)data

                       error:(NSError * __autoreleasing *)error

    {

        //设置初始值

        BOOL responseIsValid = YES;

        NSError *validationError = nil;

        //检查这个response是否包含可接受的状态码和可接受MIME类型

        if(error && !responseIsValid){

            *error = validationError;

        }

        //返回response是否有效性

        return responseIsValid;

    }

    检查这个response是否包含可接受的状态码和可接受MIME类型 

    //检查response是否为空,以及response是否是NSHTTPURLResponse类

        if(response &&[response isKindOfClass:[NSHTTPURLResponse class]]){

            // acceptableContentTypes不为空并且response的MIME类型不在可接受的范围里

            if(self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]){

                //包装错误信息

                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 = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo],validationError);

                }

                responseIsValid = NO;

            }

            // acceptableStatusCodes不为空并且acceptableStatusCodes包含response的状态码,response的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 = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo],validationError);

                responseIsValid = NO;

            }

        }

    AFImageResponseSerializer在验证response之后,会根据设置是否自动解压automaticallyInflatesResponseImage布尔值,来对imageData按图片比例返回UIImage对象 

    如果用imageWithData转成UIImage对象后,由于网络图片PNG和JPG都是压缩格式,需要解压成bitmap后才能渲染到屏幕,这时会在主线程对图片进行解压操作,这是比较耗时的,可能还会对主线程造成阻塞,所以AF还提供了AFInflatedImageFromResponseWithDataAtScale方法,对PNG和JPG解压后,返回UIImage对象,这样避免了在主线程的解压操作,不会对主线程造成卡顿

    static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response,NSData *data,CGFloat scale){

        if(!data ||[data length]== 0){

            return nil;

        }

    //创建CGImageRef

        CGImageRef imageRef = NULL;

        //用data创建CGDataProviderRef

        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);

                //如果色彩空间是CMKY,CGImageCreateWithJPEGDataProvider是不会进行处理的,也就是不进行解压,将调用AFImageWithDataAtScale返回image

                if(imageColorSpaceModel == kCGColorSpaceModelCMYK){

                    CGImageRelease(imageRef);

                    imageRef = NULL;

                }

            }

        }

        CGDataProviderRelease(dataProvider);

    //不符合解压条件的,将调用AFImageWithDataAtScale返回image,但是这里如果符合解压条件的也会调用,以及下面会对超出大小的,直接返回image,这里我觉得应该统一对不符合条件的返回image,符合条件的就不需要调用AFImageWithDataAtScale

        UIImage *image = AFImageWithDataAtScale(data,scale);

        if(!imageRef){

            if(image.images || !image){

                return image;

            }

    //这里调用CGImageCreateCopy,只会对图形本身结构进行拷贝,底层的数据是不会拷贝的

            imageRef = CGImageCreateCopy([image CGImage]);

            if(!imageRef){

                return nil;

            }

        }

    //设置图片的宽和高和存储一个像素所需要用到的字节

        size_t width = CGImageGetWidth(imageRef);

        size_t height = CGImageGetHeight(imageRef);

        size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);

    //如果图片大小宽高乘积超过1024*1024或者bitsPerComponent大于8都不解压了,因为bitmap是一直存在UIImage对象里的,可能会把内存爆了

        if(width * height > 1024 * 1024 || bitsPerComponent > 8){

            CGImageRelease(imageRef);

            return image;

        }

        //画布参数

        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

    CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);

        CGContextRelease(context);

    //再转成UIImage对象

        UIImage *inflatedImage =[[UIImage alloc]initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];

        CGImageRelease(inflatedImageRef);

        CGImageRelease(imageRef);

        return inflatedImage;

    }

    AFCompoundResponseSerializer

    -(id)responseObjectForResponse:(NSURLResponse *)response

                               data:(NSData *)data

                              error:(NSError *__autoreleasing *)error

    {

        //遍历responseSerializers                   

        for(id serializer in self.responseSerializers){

            //如果serializer不是AFHTTPResponseSerializer类,则继续

    if(![serializer isKindOfClass:[AFHTTPResponseSerializer class]]){

                continue;

            }

            NSError *serializerError = nil;

            //一层一层的调用自己的-[responseObjectForResponse:data:error:],直到返回responseObject

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

    }

    相关文章

      网友评论

          本文标题:2018-08-06

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