最详细的关于AFNetworking 4.x
版本的源码说明书,带你事无巨细地从头撸一遍AFNetworking
,本文大致分为四个部分:
- 第一部分是请求和响应的对应代码,包括
AFURLRequestSerialization
、AFURLResponseSerialization
- 第二部分是URLSession有关的代码,包括
AFURLSessionManager
和AFHTTPSessionManager
; - 第三部分是辅助的两个类:
AFSecurityPolicy
和AFNetworkReachabilityManager
; - 第四部分是
UIKit+AFNetworking
目录下的有趣内容。
本文阅读时间大概约1-1.5小时,希望能对各位有所帮助,转载请注明赤脊山的豺狼人。
AFURLRequestSerialization
这个类是Request
的序列化器,通过这个类将网络请求的相关配置生成NSURLRequest
供task
对象使用,声明文件里面有两个协议AFURLRequestSerialization
和AFMultipartFormData
;三个类AFHTTPRequestSerializer
、AFJSONRequestSerializer
、AFPropertyListRequestSerializer
,后两个是第一个的子类,我们先来看AFHTTPRequestSerializer
类。
AFHTTPRequestSerializer类
该类为遵守AFURLRequestSerialization
协议主要类,关于AFURLRequestSerialization
协议,它只定义了一个方法:
// 将request进行加工后返回新的request
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error;
通过这个协议我们能知道,AFURLRequestSerialization
这个文件主要就是用来生成request
对象。
之后我们再看一下这个AFHTTPRequestSerializer
类声明的一些关键属性:
// 字符串编码类型,默认utf-8
@property (nonatomic, assign) NSStringEncoding stringEncoding;
/* ---
以下属性是透传NSURLRequest的配置,内部通过KVO来做监视
当改变这几个属性后进行记录,会在序列化request时进行赋值
内部KVO是手动调用的,具体原因和实现可以看一下issue,蛮有意思的
*/
// 是否允许蜂窝网络
@property (nonatomic, assign) BOOL allowsCellularAccess;
// 缓存策略,具体可以看苹果文档
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
// 是否想要处理cookie
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
// 是否流水线式请求,即无需等到response就可发起新请求,默认是NO
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
// 网络服务类型,具体可以看苹果文档
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
// 超时时间
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
/* --- */
// 读取和设置请求头的属性和方法,还有Authorization的添加删除方法就不列举了
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(NSString *)field;
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;
// 默认是GET、HEAD、DELETE三种请求类型会使用query string的方式,即将参数拼接在url后面
// 除非某些特殊需求外,一般无需设置这个属性
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;
// 自定义query string的序列化方式,一般无需设置这个属性
// AF内部有一套默认的序列化方式,使用递归方式逐层进行序列化,后面会看具体代码
- (void)setQueryStringSerializationWithBlock:(nullable NSString * _Nullable (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
// 核心方法,生成包装好的request对象
// 第二个方法多了一个参数constructingBodyWithBlock,用来设置上传需要的HTTPBodyStream头,后面会具体分析是如何处理上传请求的
- (nullable NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error;
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable NSDictionary <NSString *, id> *)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
error:(NSError * _Nullable __autoreleasing *)error;
之后再看实现文件里的逻辑就会清晰一些,先看两个暴露出去的静态方法:
// 这个方法是用来进行url encode的
NSString * AFPercentEscapedStringFromString(NSString *string) {
// 根据RFC3986规定,这些字符需要进行编码
static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@";
static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
// 将上面定义的编码字符从基础库中的"url query allowed"集合中移除
NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
[allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
/*
下面开始进行编码,之所以不直接使用stringByAddingPercentEncodingWithAllowedCharacters,
主要是因为会影响到组合符号,比如emoji表情,就是由多个符号组成,直接编码会导致截断
*/
// 这是个大概值,过长string,一次处理50个字符
static NSUInteger const batchSize = 50;
NSUInteger index = 0;
NSMutableString *escaped = @"".mutableCopy;
while (index < string.length) {
NSUInteger length = MIN(string.length - index, batchSize);
NSRange range = NSMakeRange(index, length);
// rangeOfComposedCharacterSequencesForRange,这个方法会自动涵盖进全部组合符号
range = [string rangeOfComposedCharacterSequencesForRange:range];
NSString *substring = [string substringWithRange:range];
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
[escaped appendString:encoded];
index += range.length;
}
return escaped;
}
// 将字典转为query string
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
在AFQueryStringFromParameters
方法中可以看到一个叫做AFQueryStringPairsFromDictionary
的方法,这个方法会通过递归遍历,把字典深度转化为一个数组,具体看代码:
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
// 生成排序器,按照description字段进行升序排列,至于为什么要排序,原注释里面有这样的描述:
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
// 看起来是要保证query string的顺序固定,这样反序列化的时候不会出现冲突
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
if ([value isKindOfClass:[NSDictionary class]]) {
// 字典类型
NSDictionary *dictionary = value;
// 遍历字典key,取出value,value不为空时会从递归栈中生成pair数组,加入到结果数组中
// 多层次的字典嵌套,会是key[sKey][ssKey][sssKey]=xxx这种格式,一般使用时,很少会传多层嵌套的参数进来,大多的场景都是form表单
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
// 遍历数组因为没有子key,所以是key[]=xxx的格式
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
// 遍历集合与字典一样先进行排序
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
// 深层递归遍历的出栈:当遍历到字符串时,生成pair对象并加入数组
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
而转为的数组中,元素类型为AFQueryStringPair
,其实质就是一个键值对象,并提供了一个url encode方法
:
@interface AFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
...
- (NSString *)URLEncodedStringValue;
@end
@implementation AFQueryStringPair
...
- (NSString *)URLEncodedStringValue {
// 将键值进行url encode后进行拼接,空值返回encoded key
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])];
}
}
@end
之后,AFQueryStringFromParameters
方法对转化后的数组进行遍历,调用每个pair
进行url编码后,放入新数组,最后对这个新数组使用&
符号拼接成字符串返回。
以上把如何生成query string
和url encode
的逻辑看完,可以正式看AFHTTPRequestSerializer
的代码了,在它的隐式声明中有如下几个属性:
@interface AFHTTPRequestSerializer ()
// 用来保存改变过的一些属性(前文中提到的手动KVO的几个属性)
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
// 用来保存改变过的header相关属性
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
// 用来为header读写加保护的并发队列
@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue;
// 忽略...
@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
// 用来保存自定义的query string序列化操作
@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
@end
值得一提的点是requestHeaderModificationQueue
的使用:
// 读
- (NSDictionary *)HTTPRequestHeaders {
NSDictionary __block *value;
dispatch_sync(self.requestHeaderModificationQueue, ^{
value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
});
return value;
}
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
NSString __block *value;
dispatch_sync(self.requestHeaderModificationQueue, ^{
value = [self.mutableHTTPRequestHeaders valueForKey:field];
});
return value;
}
// 写
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {
dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
});
}
- (void)clearAuthorizationHeader {
dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{
[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
});
}
通过并发队列保证了读操作可以同时进行,串行保证了可以返回正确的值。在写操作时,使用了barrier
来等待所有读操作结束后再执行写操作,从而保证了操作的安全性。
我们继续往下看这个类的init
做了什么:
- (instancetype)init {
...
self.stringEncoding = NSUTF8StringEncoding;
self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
...
// 添加Accept-Language,具体acceptLanguagesComponents怎么设置的,可以看源码
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
// 设置UA
NSString *userAgent = nil;
#if TARGET_OS_IOS
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
...
#endif
if (userAgent) {
...
[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
// 设置使用query string的请求方式
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
// 添加KVO,后面手动监控的代码就不说了,感兴趣自己看下
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
return self;
}
初始化方法和一些边缘方法看完之后,就到了核心的两个方法了,先看普通的request方法:
- (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);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 手动观察的几个属性,如果改变,则进行KVC赋值
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 调用AFURLRequestSerialization协议实现方法
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
方法非常简单,主要逻辑都在AFURLRequestSerialization
协议定义的一个方法里,看一下这个协议方法的实现:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error {
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 添加header
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
// 如果存在自定义序列化则调用自定义,否则用默认的
if (self.queryStringSerialization) {
...
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
// 默认的方式把字典转query string
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// 如果是指定的GET、DELETE、HEAD其中的的请求方式,则直接将query string拼接到url中
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// 否则,进行utf-8编码后放入HTTPBODY中,注意在AFHTTPRequestSerializer中,body数据是字符串的格式,如果想用json需要用AFJSONRequestSerializer中方法生成request,后面会讲到。
if (!query) {
query = @"";
}
// 设置默认的Content-Type为application/x-www-form-urlencoded
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
再看另一个上传的request方法:
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
error:(NSError *__autoreleasing *)error {
NSParameterAssert(method);
NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
// 先调用正常的request方法,生成一个request对象
// parameters传空,就算是不传空,parameters放入HTTPBody后,也会被HTTPBodyStream覆盖掉
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
// 使用这个request对象创建FormData对象,关于这个对象内部的一些调用,之后再说
__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 {
data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
}
// 将参数转为data传入到FormData中
if (data) {
[formData appendPartWithFormData:data name:[pair.field description]];
}
}
}
// 外部通过这个block把需要上传的数据通过appendPartWithFormData方法放进FormData对象
if (block) {
block(formData);
}
// 添加需要的boundary、将上传的数据流放到HTTPBodyStream、配置Content-Type、Content-Length
return [formData requestByFinalizingMultipartFormData];
}
这里面涉及到一个对象AFStreamingMultipartFormData
这个对象的具体声明如下:
@interface AFStreamingMultipartFormData : NSObject <AFMultipartFormData>
// 传入一个初始request对象用来初始化
- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
stringEncoding:(NSStringEncoding)encoding;
// 生成最后的request,具体在上面的注释中有写到
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData;
@end
/* 隐式声明 */
@interface AFStreamingMultipartFormData ()
// 需要处理的request
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
// 分割线
@property (readwrite, nonatomic, copy) NSString *boundary;
// 核心属性,后面会讲到
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
@end
可以看到这个对象遵守了AFMultipartFormData
协议,这个协议定义了很多append
方法:
@protocol AFMultipartFormData
- (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType;
- (void)appendPartWithHeaders:(nullable NSDictionary <NSString *, NSString *> *)headers
body:(NSData *)body;
...
我们看下AFStreamingMultipartFormData
类是如何实现这些协议方法的,大致相同就不全都列出来了:
- (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType {
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
// 配置Content-Disposition和Content-Type
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
[self appendPartWithHeaders:mutableHeaders body:data];
}
- (void)appendPartWithHeaders:(NSDictionary *)headers
body:(NSData *)body {
NSParameterAssert(body);
// 初始化AFHTTPBodyPart对象
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = headers;
bodyPart.boundary = self.boundary;
bodyPart.bodyContentLength = [body length];
bodyPart.body = body;
// 将这个对象加入stream中
[self.bodyStream appendHTTPBodyPart:bodyPart];
}
一路看下来,发现AFStreamingMultipartFormData
只是相当于一个组装器,先配置好request header
,再把需要上传的不同类型数据(stream\data\file url),生成AFHTTPBodyPart
对象,将这个对象放入AFMultipartBodyStream
中。
那么AFMultipartBodyStream
又是什么呢?
@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
// 外部传入,用来控制read的大小
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
// 外部传入,用来控制每次read后的sleep时间,存在意义暂时没理解...
@property (nonatomic, assign) NSTimeInterval delay;
// 只读属性,用来计算对象里所有part的大小
@property (readonly, nonatomic, assign) unsigned long long contentLength;
// 表示对象中是否存在part
@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;
- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding;
// 设置第一个和最后一个part的分割线
- (void)setInitialAndFinalBoundaries;
// 将part对象添加进本身的数组中
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
@end
/* 隐式声明 */
@interface AFMultipartBodyStream () <NSCopying>
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
// 核心属性,内部是AFHTTPBodyPart对象实例
@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;
// HTTPBodyParts的枚举器,用来按顺序读取各个part
@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
// 记录当前正在读取的part
@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
@end
可以看到AFMultipartBodyStream
实际上是NSInputStream
的子类,并且重写了内部的方法。它本质上可以理解为是一个input stream
,但是本身并不会去read
任何url或者data,而是通过内部的HTTPBodyParts
属性,持有很多传入进来的part对象,每次read
的时候,都是去read
这些part对象中的内容。看一下具体的实现:
- (void)setInitialAndFinalBoundaries {
if ([self.HTTPBodyParts count] > 0) {
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
bodyPart.hasInitialBoundary = NO;
bodyPart.hasFinalBoundary = NO;
}
// 设置第一个part对象和最后一个part对象的分割线
[[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES];
[[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];
}
}
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {
// 外部增加part,AFStreamingMultipartFormData调用的就是这个方法
[self.HTTPBodyParts addObject:bodyPart];
}
// 重写了read方法,自身不再去read,而是使用内部的HTTPBodyParts来read
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length {
...
// 记录当前读了多少字节的数据
NSInteger totalNumberOfBytesRead = 0;
// 循环读取每个part,直到达到maxLength或者外部设置的包大小
while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
// 当前的part无数据可读,利用枚举器读取下一个part
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
// 注意此处是赋值号,而不是相等号,如果没有下一个part了,退出循环,read结束
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
break;
}
} else {
// 计算还可以读多少数据
NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead;
// 调用part的read方法,具体实现放到part中再讲
NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
if (numberOfBytesRead == -1) {
self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
break;
} else {
totalNumberOfBytesRead += numberOfBytesRead;
// 此处延迟不是很理解,可能是系统限制问题?
if (self.delay > 0.0f) {
[NSThread sleepForTimeInterval:self.delay];
}
}
}
}
return totalNumberOfBytesRead;
}
- (unsigned long long)contentLength {
unsigned long long length = 0;
// stream的大小就是所有part的大小和
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
length += [bodyPart contentLength];
}
return length;
}
stream
对象看过之后,我们来分析下part
对象,声明文件没什么好看的,相当于模型的一些属性配置,看一下隐式声明的地方:
typedef enum {
AFEncapsulationBoundaryPhase = 1, // 开始阶段
AFHeaderPhase = 2, // header处理阶段
AFBodyPhase = 3, // body处理阶段
AFFinalBoundaryPhase = 4, // 结束阶段
} AFHTTPBodyPartReadPhase;
@interface AFHTTPBodyPart () <NSCopying> {
AFHTTPBodyPartReadPhase _phase;
NSInputStream *_inputStream; // 内部持有的stream对象,读操作就是这个对象执行的
unsigned long long _phaseReadOffset; // 记录非body阶段下,读取的位置
}
// 进行下一个阶段
- (BOOL)transitionToNextPhase;
// 读数据到存储区
- (NSInteger)readData:(NSData *)data intoBuffer:(uint8_t *)buffer maxLength:(NSUInteger)length;
@end
init
方法时会调用transitionToNextPhase
方法进入开始阶段,看一下这个方法的具体实现:
- (BOOL)transitionToNextPhase {
// 强制主线程同步执行
if (![[NSThread currentThread] isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
[self transitionToNextPhase];
});
return YES;
}
// _phase初始值0,不属于任何阶段
switch (_phase) {
case AFEncapsulationBoundaryPhase:
// 从开始阶段切到header阶段
_phase = AFHeaderPhase;
break;
case AFHeaderPhase:
// 从header阶段切到body阶段
// 将input stream 加入到runloop中执行读操作
// 只有body阶段使用NSInputStream来进行流读取,其他阶段均是直接获取data字节
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
// 打开流
[self.inputStream open];
_phase = AFBodyPhase;
break;
case AFBodyPhase:
// 关闭流,并切换阶段为结束阶段
[self.inputStream close];
_phase = AFFinalBoundaryPhase;
break;
case AFFinalBoundaryPhase:
default:
_phase = AFEncapsulationBoundaryPhase;
break;
}
// 重置read位置
_phaseReadOffset = 0;
return YES;
}
这个方法内部在进去body阶段前会通过懒加载的方式,根据外部设置的body
类型,初始化input stream
对象:
- (NSInputStream *)inputStream {
if (!_inputStream) {
if ([self.body isKindOfClass:[NSData class]]) {
_inputStream = [NSInputStream inputStreamWithData:self.body];
} else if ([self.body isKindOfClass:[NSURL class]]) {
_inputStream = [NSInputStream inputStreamWithURL:self.body];
} else if ([self.body isKindOfClass:[NSInputStream class]]) {
_inputStream = self.body;
} else {
_inputStream = [NSInputStream inputStreamWithData:[NSData data]];
}
}
return _inputStream;
}
contentLength
方法会返回当前part的大小,具体就是开始标识+header大小+body大小+结束标识
,不再赘述了,我们看一下part
对象的核心方法:
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length {
NSInteger totalNumberOfBytesRead = 0;
// 开始阶段,字符串转data,将data读入存储区
if (_phase == AFEncapsulationBoundaryPhase) {
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
}
// header阶段,将header按键值拼接并添加换行符后,转data并读入存储区
if (_phase == AFHeaderPhase) {
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
}
// body阶段,使用input stream读取数据到存储区
if (_phase == AFBodyPhase) {
NSInteger numberOfBytesRead = 0;
numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
if (numberOfBytesRead == -1) {
return -1;
} else {
totalNumberOfBytesRead += numberOfBytesRead;
// 读取结束才会进入下一个阶段
if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {
[self transitionToNextPhase];
}
}
}
// 结束阶段,字符串转data,将data读入存储区
if (_phase == AFFinalBoundaryPhase) {
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
}
// 返回整个read过程中读取的字节数量
return totalNumberOfBytesRead;
}
- (NSInteger)readData:(NSData *)data intoBuffer:(uint8_t *)buffer maxLength:(NSUInteger)length {
// 确定当前阶段下,读取的起始位置
NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length));
// 将data类型的指定范围字节copy到存储区中
[data getBytes:buffer range:range];
// 设置新的起始位置
_phaseReadOffset += range.length;
// 读取完成后才会进入下一个阶段
if (((NSUInteger)_phaseReadOffset) >= [data length]) {
[self transitionToNextPhase];
}
return (NSInteger)range.length;
}
至此我们看完了上传的几个对象,重新梳理下:
AFHTTPBodyPart
作为每一个需要上传数据(字符串参数、图片、视频等等)的实体,将data
读入存储区,提供read
方法给外部使用。
AFMultipartBodyStream
作为NSInputStream
的子类,重写了整个父类的方法,表面是流,实质并不亲自去读数据,而是通过内部的一个数组来管理多个AFHTTPBodyPart
对象,最终这个对象加入请求头的HTTPBodyStream
中,当调用它的read
方法时,该类会使用枚举器来逐个读取其管理part
中的数据。
AFStreamingMultipartFormData
遵守AFMultipartFormData
协议,实现了各种类型入参(url\data\input stream
)的append
方法,内部持有着AFMultipartBodyStream
对象,每调用一次append
系列方法,都会生成一个AFHTTPBodyPart
对象并放入到AFMultipartBodyStream
对象中。而外部生成request的
multipartFormRequestWithMethod
方法,会将parameters
先放入AFStreamingMultipartFormData
中,再把这个对象通过block的形式暴露出去,供我们把想要上传的对象append
进去。
了解完上传的基本逻辑之后,让我们回到AFStreamingMultipartFormData
中剩下的两个方法:
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay {
// 设置包大小上限和读取的延迟时间
self.bodyStream.numberOfBytesInPacket = numberOfBytes;
self.bodyStream.delay = delay;
}
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
if ([self.bodyStream isEmpty]) {
return self.request;
}
// 对bodyStream中持有的首尾part添加标识符
[self.bodyStream setInitialAndFinalBoundaries];
// 将bodyStream放入HTTPBodyStream中,注意HTTPBodyStream和HTTPBody不可同时设置
[self.request setHTTPBodyStream:self.bodyStream];
// 添加必备的header
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
return self.request;
}
关于Content-Type的对照可以参考这个地址
至此这个AFHTTPRequestSerializer
大类就已经差不多分析完了,剩下两个子类比较容易理解。
AFJSONRequestSerializer类
除了GET
、HEAD
、DELETE
之外的方法,AFHTTPRequestSerializer
会把参数默认为编码后的字符串进行加入body
中,默认Content-Type
为application/x-www-form-urlencoded
,而某些接口要求使用json
格式的调用,这时就要用到AFJSONRequestSerializer
类。
这个类很简单,初始化时,默认传入一个NSJSONWritingOptions
枚举值,具体这个枚举值各个值代表,可以看一下苹果的文档,我们加速看下这个类核心方法,也是AFURLRequestSerialization
协议的实现方法:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error {
NSParameterAssert(request);
// 如果是GET、HEAD、DELETE请求方式,则调用父类的代理方法直接拼接query string
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
...
if (parameters) {
// 设置json的Content-Type类型
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
}
...
// 将参数进行json序列化为data并放入HTTPBody中
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];
if (!jsonData) {
return nil;
}
[mutableRequest setHTTPBody:jsonData];
}
return mutableRequest;
}
无非就是换了一种Content-Type
,并将参数转为json对象而已,另一个子类AFPropertyListRequestSerializer
大同小异,就不再赘述了。
AFURLResponseSerialization类
这个类是Response
的序列化器,在收到URLSession
的完成回调之后对数据进行序列化。和AFURLRequestSerialization
类似,提供了一个协议AFURLResponseSerialization
;提供了一个通用父类AFHTTPResponseSerializer
;针对iOS系统提供的四个子类:AFJSONResponseSerializer
、AFXMLParserResponseSerializer
、AFPropertyListResponseSerializer
、AFImageResponseSerializer
;一个可以包含多个序列化器的混合类AFCompoundResponseSerializer
。
AFHTTPResponseSerializer类
AFHTTPResponseSerializer
是遵守AFURLResponseSerialization
协议的主要类,这个协议也只有一个方法:
// 传入response对象和data对象,返回序列化之后的对象
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;
通过这个协议方法我们能看到,AFHTTPResponseSerializer
主要就是将请求返回的response
对象转成我们希望的数据类型,比如NSDictionary
或NSArray
。
AFHTTPResponseSerializer
类里面的属性方法不是很多,我们一一看下:
// 可以认定为成功的接口状态码,默认是[200, 300)的范围,如果是nil的话,任何状态码都认定为成功
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
// 可以认定为成功的MIME类型,HTTP这个类默认是nil,即任何MIME类型都是成功
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;
// 按顺序验证MIME类型和状态码是否有效,无效时会加工外部传入的error
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;
可见这个声明类很简洁,算上定义的协议方法也才两个属性两个方法,静态方法先略过,具体看类实现方法:
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error {
BOOL responseIsValid = YES;
NSError *validationError = nil;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
// 如果acceptableContentTypes不为空且response的MIME不在其中,那么会把错误信息加入error中
if (self.acceptableContentTypes && -
![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
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;
}
// 判断完MIME之后判断StatusCode,逻辑相同
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
...
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}
可以看到validateResponse
逻辑非常简单,其中涉及到一个静态方法AFErrorWithUnderlyingError
:
// 将underlyingError放进error.userInfo对应的key中
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
接着再看,协议方法的实现:
// 方法更简单,调用validateResponse的目的主要是包装error供外部使用
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error {
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}
以上就是这个response
通用父类的实现了。
AFJSONResponseSerializer类
日常我们使用的更多的是JSON
类型的接口返回,所以用的也是AFJSONResponseSerializer
类,我们看一下这个类的接口定义:
// JSON数据的read操作项,具体看下苹果文档吧
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
// 是否将Null对象从JSON中移除,默认为NO
@property (nonatomic, assign) BOOL removesKeysWithNullValues;
// 根据传入的read操作项来初始化
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions;
相对父类来说,AFJSONResponseSerializer
类主要增加了相对于JSON
的配置,看一下具体的实现:
- (instancetype)init {
...
// 初始化的时候默认的MIME类型
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error {
// 调用父类的validateResponse方法,判定当前response是否有效
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
...
NSError *serializationError = nil;
// 进行json序列化
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject) {
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
// 如果需要移除Null,则调用AFJSONObjectByRemovingKeysWithNullValues方法
if (self.removesKeysWithNullValues) {
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
return responseObject;
}
方法中调用了一个移除Null对象的静态方法,同样是递归深层遍历:
id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
if ([JSONObject isKindOfClass:[NSArray class]]) {
// 如果是数组对象,则遍历数组,如果不是Null对象则继续递归调用
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
if (![value isEqual:[NSNull null]]) {
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
}
// 如果有NSJSONReadingMutableContainers项,则直接使用可变数组,否则生成不可变数组
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
// 如果是字典对象,则遍历字典,如果是Null或者nil,则移除,如果是字典或者数组对象,继续递归调用
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
id value = (NSDictionary *)JSONObject[key];
if (!value || [value isEqual:[NSNull null]]) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
}
}
// 如果有NSJSONReadingMutableContainers项,则直接使用可变字典,否则生成不可变字典
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return JSONObject;
}
该类实质上就是在实现协议方法时,由什么都不干,变成了使用JSON
序列化方法而已,非常简单。
后面的AFXMLParserResponseSerializer
类也是同理,使用了NSXMLParser
来初始化XML
对象,MIME类型允许的是application/xml
和text/xml
类型。
AFPropertyListResponseSerializer
类,使用NSPropertyListSerialization
来进行初始化,MIME类型允许的是application/x-plist
类型。
AFImageResponseSerializer类
接下来我们看下图片类型的序列化器,如果接口MIME类型是如下则使用这个类进行解析:
image/tiff image/jpeg image/gif image/png image/ico image/x-icon image/bmp image/x-bmp image/x-xbitmap image/x-win-bitmap
当然你也可以直接用AFHTTPResponseSerializer
来返回图片数据,不过这个类对图片数据进行了优化,可以看下它的声明:
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
// 图片比例,默认是根据你的屏幕比例来计算,比如pro max就是3.0,生成的图片会压缩成三分之一,这个值影响image.size属性
@property (nonatomic, assign) CGFloat imageScale;
// 是否自动Inflate响应的图片数据,可以理解为将网络上的压缩图片进行必要信息填充,这个操作是在AFN自己的子线程完成的。默认是YES
// 如果设为NO,则直接返回未填充的UIImage对象,交给UI控件进行填充,这个填充动作是在主线程完成的,所以会造成堵塞。
@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;
#endif
具体如何实现的要看一下内部逻辑:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error {
...
#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
...
#endif
return nil;
}
省略不重要的代码,核心就是根据automaticallyInflatesResponseImage
来选择使用不同的方法,我们先看下AFImageWithDataAtScale
的实现:
static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
// 自定义了类别方法来实现data转image
UIImage *image = [UIImage af_safeImageWithData:data];
if (image.images) {
// 如果是动态图片组,直接返回image
return image;
}
// 否则将image按照scale来进行转换
return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
}
关于UIImage
的类别方法,比较简单:
@interface UIImage (AFNetworkingSafeImageLoading)
+ (UIImage *)af_safeImageWithData:(NSData *)data;
@end
static NSLock* imageLock = nil;
@implementation UIImage (AFNetworkingSafeImageLoading)
+ (UIImage *)af_safeImageWithData:(NSData *)data {
UIImage* image = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
imageLock = [[NSLock alloc] init];
});
// 初始化一次lock后,每次调用该方法都会先加锁,再进行data转image
[imageLock lock];
image = [UIImage imageWithData:data];
[imageLock unlock];
return image;
}
@end
不启用automaticallyInflatesResponseImage
时直接将响应数据转为UIImage
对象返回,如果启用该属性,则进行AFInflatedImageFromResponseWithDataAtScale
方法:
static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
if (!data || [data length] == 0) {
return nil;
}
/* 将data转为CGImageRef对象 */
CGImageRef imageRef = NULL;
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
// 只处理png和jpeg这两种压缩图片格式
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);
// 针对于CMYK的编码方式不作处理,具体原因不太清楚
if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
CGImageRelease(imageRef);
imageRef = NULL;
}
}
}
CGDataProviderRelease(dataProvider);
/* --- */
/* 特殊处理:
如果imageRef对象不存在直接使用AFImageWithDataAtScale生成UIImage返回;
如果图片资源的size大于1024*1024,或者色彩位数大于8,同样使用AFImageWithDataAtScale生成UIImage返回
*/
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;
}
/* --- */
/* 图片信息填充 */
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);
/* 将填充后的图片转为指定scale的UIImage对象 */
UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];
CGImageRelease(inflatedImageRef);
CGImageRelease(imageRef);
return inflatedImage;
}
因为不清楚图片处理的知识,对于这个方法我也讲不出太多内容,只能大概理解是为了将UI底层做的事情,提前拿到了相应数据处理的阶段,可以改善绘制性能。
AFCompoundResponseSerializer类
这个类比较简单,作为序列化器的混合类,可以将不同的子类加入其中组合使用:
// 初始化时可以传入一个序列化器的数组,序列化时遍历使用
@property (readonly, nonatomic, copy) NSArray <id<AFURLResponseSerialization>> *responseSerializers;
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<AFURLResponseSerialization>> *)responseSerializers;
具体实现也非常简单,看一下代码:
- (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;
// 调用每个序列化器的协议方法,哪个返回了object,就终止遍历并返回这个object
id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
if (responseObject) {
if (error) {
*error = AFErrorWithUnderlyingError(serializerError, *error);
}
return responseObject;
}
}
// 用父类,即AFHTTPResponseSerializer,进行兜底
return [super responseObjectForResponse:response data:data error:error];
}