#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];
}
网友评论