AFNetworking 4.x 最全的源码说明书(一)

1,807 阅读19分钟

最详细的关于AFNetworking 4.x版本的源码说明书,带你事无巨细地从头撸一遍AFNetworking ,本文大致分为四个部分:

  • 第一部分是请求和响应的对应代码,包括AFURLRequestSerializationAFURLResponseSerialization
  • 第二部分是URLSession有关的代码,包括AFURLSessionManagerAFHTTPSessionManager
  • 第三部分是辅助的两个类:AFSecurityPolicyAFNetworkReachabilityManager
  • 第四部分UIKit+AFNetworking目录下的有趣内容。

本文阅读时间大概约1-1.5小时,希望能对各位有所帮助,转载请注明赤脊山的豺狼人

AFURLRequestSerialization

这个类是Request的序列化器,通过这个类将网络请求的相关配置生成NSURLRequesttask对象使用,声明文件里面有两个协议AFURLRequestSerialization AFMultipartFormData ;三个类AFHTTPRequestSerializer AFJSONRequestSerializerAFPropertyListRequestSerializer ,后两个是第一个的子类,我们先来看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 stringurl 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类

除了GETHEADDELETE之外的方法,AFHTTPRequestSerializer 会把参数默认为编码后的字符串进行加入body中,默认Content-Typeapplication/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系统提供的四个子类:AFJSONResponseSerializerAFXMLParserResponseSerializerAFPropertyListResponseSerializerAFImageResponseSerializer;一个可以包含多个序列化器的混合类AFCompoundResponseSerializer

AFHTTPResponseSerializer类

AFHTTPResponseSerializer 是遵守AFURLResponseSerialization 协议的主要类,这个协议也只有一个方法:

// 传入response对象和data对象,返回序列化之后的对象
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                                    data:(nullable NSData *)data
                                   error:(NSError * _Nullable __autoreleasing *)error;

通过这个协议方法我们能看到,AFHTTPResponseSerializer 主要就是将请求返回的response对象转成我们希望的数据类型,比如NSDictionaryNSArrayAFHTTPResponseSerializer 类里面的属性方法不是很多,我们一一看下:

// 可以认定为成功的接口状态码,默认是[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/xmltext/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];
}