AFNetworking主线梳理(二)

上一篇整理了AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];,这一篇整理AFN的重头戏:请求。从[manager POST:parameters:progress:success:failure:]开始梳理。


AFHTTPSessionManager 部分

日常开发中请求主要就是GET和POST两种

GET 和 POST 的声明

GET声明

- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                            parameters:(nullable id)parameters
                              progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

POST声明

- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

GET 和 POST 的实现

GET实现

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{

    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];

    [dataTask resume];

    return dataTask;
}

POST实现

- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(id)parameters
                      progress:(void (^)(NSProgress * _Nonnull))uploadProgress
                       success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                       failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];

    [dataTask resume];

    return dataTask;
}

分解说明

  1. 首先要理解,AFHTTPSessionManager类的GET和POST方法内部实现的核心是:调用dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:方法来获取NSURLSessionDataTask。
  2. 这个NSURLSessionDataTask才是真正可以发起网络请求的类。而开启请求的真正方法是resume,当NSURLSessionDataTask调用了resume,网络请求才发起。

GET、POST 核心方法dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:

dataTaskWithHTTPMethod...的实现

第一部分解析

一句话描述 : 第一部分做的事情就是创建一个NSMutableURLRequest。

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) {
        dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
            failure(nil, serializationError);
        });
    }

    return nil;
}

先说整体逻辑

  1. 第一部分创建的NSMutableURLRequest是要提供给第二部分使用。
  2. 这个NSMutableURLRequest不是AFHTTPSessionManager类自己创建的,是通过自己的属性requestSerializer调用requestWithMethod:URLString:parameters:error:方法创建的。如果NSMutableURLRequest创建失败,则直接回调给开发者网络请求失败。

分解说明:

  1. 属性requestSerializer:requestSerializer是AFHTTPSessionManager类自己独有的属性,父类AFHTTPRequestSerializer没有这个属性。requestSerializer在AFHTTPSessionManager初始化的时候已经默认进行了初始化,开发者也可以随时修改此属性。初始化详情可以参考AFNetworking主线梳理(一)中的介绍。

  2. 参数completionQueue:失败回调的队列默认是主队列,如果设置了completionQueue,则走设置的队列。completionQueue是AFHTTPSessionManager父类AFURLSessionManager的属性,需要开发者来设置。


接下来,解析requestWithMethod:URLString:parameters:error:,需要跳转到AFHTTPRequestSerializer 部分(往下翻)。

第二部分解析

一句话描述:
通过调用dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法来创建一个NSURLSessionDataTask实例对象。

__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
                      uploadProgress:uploadProgress
                    downloadProgress:downloadProgress
                   completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
    if (error) {
        if (failure) {
            failure(dataTask, error);
        }
    } else {
        if (success) {
            success(dataTask, responseObject);
        }
    }
}];


分解说明

  1. NSURLSessionDataTask就是前面说的GET和POST方法核心实现所需要的task。
  2. dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法所需要的参数request就是第一部分创建的NSMutableURLRequest实例。
  3. AFHTTPSessionManager自己并没有实现dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法,是它的父类AFURLSessionManager实现了这个方法。


接下来,解析dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:方法,跳转到AFURLSessionManager 部分(使劲往下翻)。


AFHTTPRequestSerializer 部分

接续 GET、POST 核心方法dataTaskWithHTTPMethod 方法第一部分,AFHTTPRequestSerializer的requestWithMethod:URLString:parameters:error:方法最终目的是创建一个NSMutableURLRequest实例并返回。

requestWithMethod:URLString:parameters:error: 方法解析

以下简称:requestWithMethod方法。

requestWithMethod...方法的实现

requestWithMethod方法整体逻辑:

  1. 使用传入的参数URLString创建一个NSURL *url
  2. 使用url创建一个NSMutableURLRequest *mutableRequest
  3. 将传入的参数 method 保存到 mutableRequest 的 HTTPMethod 属性中
  4. 第一部分:简单讲就是在配置NSMutableURLRequest的各种参数。
  5. 第二部分:简单讲其实也是在配置NSMutableURLRequest的各种参数。
  6. NSMutableURLRequest配置完成后,返回其实例mutableRequest。

第一部分

整体逻辑:

  1. AFHTTPRequestSerializerObservedKeyPaths()函数会创建并返回一个数组,数组中有6个属性名的字符串(keyPath)。
  2. for in 遍历这个数组,取出每一个属性名的字符串(keyPath)。
  3. 判断self.mutableObservedChangedKeyPaths(一个NSMutableSet)中是否包含keyPath,如果包含这个keyPath,则把AFHTTPRequestSerializer自己这个属性的值赋值给mutableRequest。

分解说明:

  1. AFHTTPRequestSerializer有6个特定的属性,分别是:

    1. @property (nonatomic, assign) BOOL allowsCellularAccess;
    2. @property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
    3. @property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
    4. @property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
    5. @property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
    6. @property (nonatomic, assign) NSTimeInterval timeoutInterval;
  2. 这6个属性开发者可以读写,而这6个属性与NSMutableURLRequest的属性一一对应,换句话说AFHTTPRequestSerializer的6个属性最终目的是开发者设置具体值,然后传递给NSMutableURLRequest,只不过开发者从设置值到mutableRequest接收到值的过程略显复杂。

  3. 在AFHTTPRequestSerializer的init方法中依次对6个属性添加了键值观察(KVO)。
    init方法片段:

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }

    ...省略...
    
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
        }
    }

    return self;
}

  1. AFHTTPRequestSerializer重写了每一个属性的setter,成员变量Ivar赋值前后加了KVO的willChangeValueForKeydidChangeValueForKey来手动触发KVO。
    AFHTTPRequestSerializer的6个属性的setter实现方式完全一致,这里只贴两个属性的setter源码:
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
    [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
    _allowsCellularAccess = allowsCellularAccess;
    [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}

- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy {
    [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
    _cachePolicy = cachePolicy;
    [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
}

等等......

  1. AFHTTPRequestSerializerObservedKeyPaths()函数:
    下的源码就是AFHTTPRequestSerializerObservedKeyPaths()函数的实现,其实很简单,只是创建了一个单例数组,数组中是这个6个属性的getter由selector转成的string。
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
    });

    return _AFHTTPRequestSerializerObservedKeyPaths;
}

  1. self.mutableObservedChangedKeyPaths 解析
    1. self.mutableObservedChangedKeyPaths是什么?
      • 答:AFHTTPRequestSerializer的一个属性。用来保存被开发者赋值的6个属性中的一个或多个(也可以没有)。
      @property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
      
    2. self.mutableObservedChangedKeyPaths是在什么地方被初始化的?
      • 答:是在AFHTTPRequestSerializer的init方法中被初始化的。
      self.mutableObservedChangedKeyPaths = [NSMutableSet set];
      
    3. 案例Demo中并没有调用AFHTTPRequestSerializer的init方法,AFHTTPRequestSerializer的init方法是什么时候调用的?
      • 答:完整的调用顺序是:[AFHTTPSessionManager manager] => [AFHTTPRequestSerializer serializer] => [[self alloc] init]。这里self是指AFHTTPRequestSerializer。
    4. self.mutableObservedChangedKeyPaths在什么时候添加的元素?
      • 答:继续往下看。


第一部分总结:

AFHTTPRequestSerializer早在初始化的时候就对6个属性依次添加了键值观察(KVO)。如果我们在调用 AFHTTPSessionManager 的 GET 方法或者 POST 方法之前对这6个属性赋了值,就会调用属性的setter,接着会触发KVO的通知,来到KVO的observeValueForKeyPath:ofObject:change:context:回调方法。

#pragma mark - NSKeyValueObserving

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
        return NO;
    }

    return [super automaticallyNotifiesObserversForKey:key];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(__unused id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (context == AFHTTPRequestSerializerObserverContext) {
        if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
            [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else {
            [self.mutableObservedChangedKeyPaths addObject:keyPath];
        }
    }
}

在KVO的observeValueForKeyPath:ofObject:change:context:回调方法中,根据值是否为空,来操作self.mutableObservedChangedKeyPaths是添加该keyPath还是移除。

我们假定开发者在调用GET或POST方法请求前设置了timeoutInterval属性:

  1. 走timeoutInterval属性的setter
  2. 触发KVO的通知,来到KVO的observeValueForKeyPath:ofObject:change:context:回调方法
  3. timeoutInterval属性被添加到了mutableObservedChangedKeyPaths集合中
  4. 接下来就是调用AFHTTPSessionManager的 GET 或 POST 方法发起请求
  5. 然后就会来到此处(requestWithMethod:URLString:parameters:error:方法的第一部分,就是现在在解析的这里)
  6. 接着把timeoutInterval赋值给NSMutableURLRequest。

以上就是第一部分做的全部事情。


一些补充:

  • automaticallyNotifiesObserversForKey:函数用来根据条件阻止KVO发出通知,系统提供的方法,AFN只是进行了重写。
  • 集合(set)自带去重的功能,所以当6个属性被多次赋值时,也就是多次触发observeValueForKeyPath,mutableObservedChangedKeyPaths集合(set)中不会出现多个同样的keyPath。

为什么AFN要手动触发KVO,而不是自动触发KVO ?

答:AFN的早期版本也使用KVO观察这6个属性,有人发现在单元测试中会出现崩溃,所以后来增加了automaticallyNotifiesObserversForKey方法来阻止这6个属性通过KVO自动发送通知。但是很快人们发现这样做,这6个属性使用开始出现问题,会有设置后无效的情况。于是作者又改了回去,删了automaticallyNotifiesObserversForKey方法,但是问题又绕回原地了。因此为了能在单元测试中正常使用,并且这6个属性的使用不能受到影响,即正常可以使用KVO,于是增加了6个属性的setter,在setter里面手动触发KVO。

第二部分 (高能预警)

一句话描述:
根据请求方法(GET、POST)的不同,将Parameters按照每种方法的要求组装到NSMutableURLRequest的实例mutableRequest中去。即GET方法是将参数处理后拼接到URL中,POST方法是将参数处理后设置到请求体中。

第二部分核心方法:requestBySerializingRequest:withParameters:error:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    
    // 这部分起个名:self.HTTPRequestHeaders部分
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    // 这部分起个名:处理传入的parameters部分
    NSString *query = nil;
    if (parameters) {
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        } else {
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    // 这部分起个名:使用query装配mutableRequest部分
    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 {
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}


在源码的注释中为了方便叙述我给划分成了3个部分,分别是:

  1. self.HTTPRequestHeaders部分
  2. 处理传入的parameters部分
  3. 使用query装配mutableRequest部分

按照代码顺序,逐步解析:

self.HTTPRequestHeaders部分

[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
    if (![request valueForHTTPHeaderField:field]) {
        [mutableRequest setValue:value forHTTPHeaderField:field];
    }
}];

一句话描述:
遍历self.HTTPRequestHeaders,如果我们创建的新 NSURLRequest *request 中没有开发者指定(init中也指定了默认)的请求头字段和值,那么把没有的这部分开发者指定的的请求头字段和值保存一份到request中。

整体逻辑

  1. 遍历self.HTTPRequestHeaders
    • self.HTTPRequestHeaders只有初始化时AFN默认添加的Accept-LanguageUser-Agent两个键值对。
  2. 如果我们创建的新 NSURLRequest *request 中开发者指定了Accept-LanguageUser-Agent以外的请求头键值对,那么把这些Accept-LanguageUser-Agent以外的请求头键值对保存一份到request中。

分解说明

  1. self.HTTPRequestHeaders 的声明在 AFHTTPRequestSerializer.h:

    @property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;
    
  2. self.HTTPRequestHeaders 的取值方法,即getter:

    - (NSDictionary *)HTTPRequestHeaders {
        NSDictionary __block *value;
        dispatch_sync(self.requestHeaderModificationQueue, ^{
            value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
        });
        return value;
    }
    

    getter解析:

    • self.HTTPRequestHeaders 的值完全来自于 self.mutableHTTPRequestHeaders
    • requestHeaderModificationQueue 在 AFHTTPRequestSerializer 的 init 方法中初始化,是一个并发队列。具体请参考AFNetworking主线梳理(一)AFHTTPRequestSerializer 部分。
    self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue",  DISPATCH_QUEUE_CONCURRENT);
    
  3. self.mutableHTTPRequestHeaders

    • self.mutableHTTPRequestHeaders 的声明在 AFHTTPRequestSerializer.m:
    @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
    
    • self.mutableHTTPRequestHeaders 在 AFHTTPRequestSerializer 的 init 方法中初始化
    self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
    
    • self.mutableHTTPRequestHeaders 如何被赋值?
      • self.mutableHTTPRequestHeaders 是在 setValue:forHTTPHeaderField: 方法中间接赋值的。
      • requestHeaderModificationQueue 在 AFHTTPRequestSerializer 的 init 方法中初始化,和前面第2小点的"self.HTTPRequestHeaders取值方法"用的是同一个并发队列。具体请参考AFNetworking主线梳理(一)AFHTTPRequestSerializer 部分。
  1. 那么 self.mutableHTTPRequestHeaders 是什么时候被赋上默认值的呢?
    • 答:在 AFHTTPRequestSerializer的init 方法中,分两次给 self.mutableHTTPRequestHeaders 赋了默认值,分别是:Accept-Language、User-Agent。具体请参考AFNetworking主线梳理(一)AFHTTPRequestSerializer 部分。

至此,“第二部分 (高能预警)” => “self.HTTPRequestHeaders部分”的使命完成。

问:开发者如何给request指定请求头的字段和值呢?
答:通过 AFHTTPRequestSerializer 的 setValue:forHTTPHeaderField: 方法。


处理传入的parameters部分

NSString *query = nil;
if (parameters) {
    if (self.queryStringSerialization) {
        NSError *serializationError;
        query = self.queryStringSerialization(request, parameters, &serializationError);

        if (serializationError) {
            if (error) {
                *error = serializationError;
            }

            return nil;
        }
    } else {
        switch (self.queryStringSerializationStyle) {
            case AFHTTPRequestQueryStringDefaultStyle:
                query = AFQueryStringFromParameters(parameters);
                break;
        }
    }
}

一句话描述:“处理传入的parameters部分” 就是将parameters按照规则拼接成query string。

假定开发者传入的parameters为字典{"key1" : "value1", "key2" : "value2"},那么query = "key1=value1&key2=value2"。

接下来我们看看parameters是如何被拼接成query的。

“处理传入的parameters部分” 按照 if else被自然分割成两部分,先看 if 这部分。

if 分支整体逻辑:

  1. 如果 self.queryStringSerialization(一个block)存在,即开发者实现了这个block,则执行 if 分支内的语句。
  2. 把 request、parameters 以及 *error 传递给self.queryStringSerialization这个block,由block处理完成后,返回query。

分解说明:

  1. self.queryStringSerialization 是一个block,需要开发者实现,声明以及赋值方法 API 如下
// AFURLRequestSerialization.h
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;

// AFURLRequestSerialization.m
typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error);

@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;

- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block {
    self.queryStringSerialization = block;
}
  1. self.queryStringSerialization 是什么时候、什么地方被实现的?
    答:没有默认的初始化,需要开发者去实现此block。如果开发者没有实现这个block,就不会走 if 分支。

  2. self.queryStringSerialization 的作用
    答:这个block的意义所在,是当开发者如果需要自定义 query string 的拼接规则,则用block传过来的request、parameters 以及 error 自行拼接一个 query string 并返回。

  3. NSError *__autoreleasing *error 干啥的?
    答案:如果开发者在拼接 query string 过程中出现错误,想要抛出Error,就可以利用这个指针的指针来直接传递error。


else 分支整体逻辑:

可以说 else 分支直接可以看成 query = AFQueryStringFromParameters(parameters); 这一句代码最为合适。

如果开发者没有使用(实现)self.queryStringSerialization,则会走 else分支,else分支也是最普通、最常用的分支。

分解说明:

  1. 虽然 else 分支是一整个switch控制的,但是AFHTTPRequestQueryStringSerializationStyle枚举值只有一个而且还是 0 (NSUInteger)。也就是说开发者设置这个Style也好不设置也好,最终都会走switch的第一个case分支,目前AFN的版本中你没得选。
typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
    AFHTTPRequestQueryStringDefaultStyle = 0,
};
  1. self.queryStringSerializationStyle 是一个枚举值,需要开发者指定,声明以及赋值方法 API 如下
// AFURLRequestSerialization.h
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;

// AFURLRequestSerialization.m
@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;

- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style {
    self.queryStringSerializationStyle = style;
    self.queryStringSerialization = nil;
}


重点!!!
else 分支的核心函数 AFQueryStringFromParameters()

  1. AFQueryStringFromParameters() 函数

一句话描述:按照规则把parameters中所有的key和value拼接成一个字符串。

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

    return [mutablePairs componentsJoinedByString:@"&"];
}


详细逻辑:

  1. 创建一个空的可变数组,NSMutableArray *mutablePairs
  2. 调用AFQueryStringPairsFromDictionary()函数获得一个数组,一个装满AFQueryStringPair实例对象的数组。AFQueryStringPair是AFURLRequestSerialization的私有类,具体实现贴在文末(往下翻)。
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
  1. AFQueryStringPairsFromDictionary()函数直接调用AFQueryStringPairsFromKeyAndValue()函数
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
    // 升序排序
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];

    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)];
            }
        }
    } else if ([value isKindOfClass:[NSArray class]]) {
        NSArray *array = value;
        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 { // 这个分支才是主要分支
        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
    }

    return mutableQueryStringComponents;
}
  1. AFQueryStringPairsFromKeyAndValue()函数采用递归调用方式将传进来的value(假定是字典)一层一层剥开,直到value不再是任何类型的集合,然后组装成AFQueryStringPair实例对象添加到数组中,返回数组。
    • 4.1 如果传进来的value是一个字典套字典的结构:value = {data : {adSource : Admob}}。如果传进来的key是空,则第一次递归不重新组装函数的参数key,直接用下一层的key当做函数递归时的key。
    • 4.2 假定传进来的key = "Hello"
    • 4.3 先把value的allKeys进行升序排序,形成一个新的数组。
    • 4.4 for in 遍历这个新的数组,取出每一个key(data这一层),使用这个key从字典取出{adSource : Admob},即先把value这个字典剥开一层。
    • 4.5 重新组装AFQueryStringPairsFromKeyAndValue(NSString *key, id value)函数的参数,进行递归。
    • 4.6 若函数传进来的key为Hello,则递归时key="Hello[data]",value={adSource : Admob}。若函数传进来的key为空,则递归时key="data",value={adSource : Admob}
    • 4.7 递归到最后,会走到AFQueryStringPairsFromKeyAndValue()函数的最后一个分支,即不是任何一个集合的else分支里,此例的最终key="Hello[data][adSource]"或者"data[adSource]",最终的value="Admob",用这个键值对初始化一个AFQueryStringPair实例对象,添加到数组,返回此数组。
  2. 经过3和4已经获得了2所需要的装满AFQueryStringPair实例对象的数组,for in 遍历这个数组中的每一个AFQueryStringPair实例对象,将[AFQueryStringPair URLEncodedStringValue]的返回值添加到数组。
  3. 第5步的数组组装完成后,大致应该是这样("data[adSource]=Admob", "data[adType]=3")。然后将这个数组使用"&"拆分组装成字符串,返回这个字符串,这个字符串就是query string。这个query大致长这样:"data[adSource]=Admob& data[adType]=3"
  4. query组装完成,AFQueryStringFromParameters()函数完成使命,返回query

至此,AFQueryStringFromParameters()函数的使命已完成,返回了拼接好的query。
  把文章往上翻🤓
这个query由else分支的query接收,也就是说else分支的使命也已完成。
  再把文章往上翻🤓
if分支是由开发者实现的self.queryStringSerialization block来完成query的拼接。ifelse 分支都可完成query的拼接。
  再一次把文章往上翻🤓
“第二部分 (高能预警)” => “处理传入的parameters部分”就全部完成了,接下来该说一说“第二部分 (高能预警)” => “使用query装配mutableRequest部分”


使用query装配mutableRequest部分

一句话描述:将query装配到mutableRequest里。

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 {
    // #2864: an empty string is a valid x-www-form-urlencoded payload
    if (!query) {
        query = @"";
    }
    if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
        [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    }
    [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}

详细逻辑:

  1. 这一部分仍然是 if else 天然分割成的两个分支。判断条件是:self.HTTPMethodsEncodingParametersInURI(NSSet)里是否包含 [request HTTPMethod]的请求方法。

  2. if 分支:如果[request HTTPMethod]的方法是GETDELETEHEAD这三种,则属于包含在里面。那么把请求时传进来的URL拆分,连同query一起,添加进去,将URL重新组装,再赋值给mutableRequest.URL,比如GET方法的请求参数就是拼接在URL里面。

  3. else 分支:如果[request HTTPMethod]的方法是POST,则不包含在里面。那么就把query按照UTF8编码成data后,调用[mutableRequest setHTTPBody:]POST 的请求参数(query)设置到请求体中。


分解说明:

  1. self.HTTPMethodsEncodingParametersInURI 是一个集合(NSSet),AFN给了默认值GET, HEAD, DELETE。开发者也可以自己指定。头文件里声明,AFJSONRequestSerializer 的 init 方法设置的默认值:
// AFURLRequestSerialization.h
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;

// AFURLRequestSerialization.m => init 方法

self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
  1. [request HTTPMethod] 是什么?
    答:[request HTTPMethod] 方法返回的是开发者设置到request的请求方法,诸如 GET、POST 等。

  2. request的请求方法是什么时候设置的?
    答:完整的调用顺序是

    1. AFHTTPSessionManager:GET或者POST方法
    2. AFHTTPSessionManager:[dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:]方法
    3. AFHTTPRequestSerializer:[requestWithMethod:URLString:parameters:error:] 方法
    4. AFHTTPRequestSerializer:mutableRequest.HTTPMethod = method;
  3. self.stringEncoding 是什么?
    答:字符串编码,默认为NSUTF8StringEncoding

  4. self.stringEncoding 是什么时候设置的默认值?
    答:AFHTTPRequestSerializer 的 init 方法中。

self.stringEncoding = NSUTF8StringEncoding;

至此,“第二部分 (高能预警)” => “使用query装配mutableRequest部分” 使命已完成,已成功的将query string装配到mutableRequest里。

同时也意味着“第二部分 (高能预警)”这一部分使命也已完成。

再加上第一部分,[requestWithMethod:URLString:parameters:error:]方法的使命也已完成,创建一个NSMutableURLRequest实例,并返回给 AFHTTPSessionManager 的 [dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:] 方法。

AFHTTPRequestSerializer 部分使命全部完成。AFHTTPRequestSerializer 部分已经负责创建 NSURLRequest 并返回给 AFHTTPSessionManager 的 dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure: 第一部分。


AFURLSessionManager 部分

接续 AFHTTPSessionManager 的 dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure: 第二部分,AFURLSessionManager 部分的dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:函数主要任务就是创建一个 NSURLSessionDataTask 实例对象,并返回。

dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: 函数解析

- (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];
    });

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

    return dataTask;
}

整体逻辑

  1. 创建一个局域变量:NSURLSessionDataTask *dataTask,初始化为 nil 。
  2. 实现 url_session_manager_create_task_safely 函数的block。
  3. 在block的回调中,通过属性 session 调用 dataTaskWithRequest:方法。
  4. dataTaskWithRequest:方法会创建一个 NSURLSessionDataTask 实例对象,并返回。
  5. 调用 addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 方法(参考 AFNetworking主线梳理(一))。

分解说明

  1. url_session_manager_create_task_safely 函数
    • 函数的实现
    static void url_session_manager_create_task_safely(dispatch_block_t block) {
        if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
            // Fix of bug
            // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
            // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
            dispatch_sync(url_session_manager_creation_queue(), block);
        } else {
            block();
        }
    }
    
    • 函数的主要逻辑:
      只有if else, 如果版本号小于iOS修复版本号,则需要把当前任务同步的添加到一个串行队列中,保证不发生并发。否则,直接调用block回调回去。
    • 函数的block回调:
      函数的block回调中的代码最终还是顺序执行的,无论是在if还是else里都是同步顺序执行。简单讲就是dataTask = [self.session dataTaskWithRequest:request];这句代码是顺序执行的,就执行顺序而言与在block外无异。
    • 函数的参数:
      url_session_manager_creation_queue()函数创建并返回一个类似单例管理的串行队列。
    static dispatch_queue_t url_session_manager_creation_queue() {
        static dispatch_queue_t af_url_session_manager_creation_queue;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
        });
    
        return af_url_session_manager_creation_queue;
    }
    

  1. self.session
    • session是AFURLSessionManager的属性
    @property (readonly, nonatomic, strong) NSURLSession *session;
    
    • session属性是什么时候初始化的?
      在AFURLSessionManager初始化方法initWithSessionConfiguration:中进行的初始化,由AFN指定默认配置。具体请参考AFNetworking主线梳理(一)
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    

  1. dataTaskWithRequest:request 是 NSURLSession 的方法,利用入参NSURLRequest创建一个 NSURLSessionDataTask 实例对象并返回。

问:为什么创建dataTask的代码要在url_session_manager_create_task_safely函数的block回调中执行?
答:为了解决 iOS 7 存在的bug。在 iOS 7 某些版本中如果并发创建NSURLSessionTask,会造成 NSURLSessionTask 的 taskIdentifier不唯一(有重复的),而AFN依赖 taskIdentifier 保存了很多信息,会造成混乱。

问:为什么url_session_manager_creation_queue()函数用单例保存队列?
个人猜测:因为该队列职责单一,无需多次重复创建新队列。网络请求多数情况下是高频的,所以多次创建不是一个好的选择。


AFQueryStringPair 部分

#pragma mark -

@interface AFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;

- (instancetype)initWithField:(id)field value:(id)value;

- (NSString *)URLEncodedStringValue;
@end

@implementation AFQueryStringPair

- (instancetype)initWithField:(id)field value:(id)value {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.field = field;
    self.value = value;

    return self;
}

- (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])];
    }
}

@end

AFPercentEscapedStringFromString()没什么特殊,就是把传进去的字符串做了百分号编码的处理,防止网络请求时出现不符合规则的字符。处理完成后,返回安全的字符串。

推荐阅读更多精彩内容