iOS-三方库-AFNetworking

一. AFN简介

1. HTTP请求解决方案

在iOS中,常见的发送HTTP请求(GET和POST)的解决方案有:

  1. 苹果原生(自带)
    NSURLConnection:用法简单,最古老最经典最直接的一种方案
    NSURLSession:iOS7新出的技术,功能比NSURLConnection更加强大
    CFNetwork:NSURL的底层,纯C语言
  2. 第三方框架
    ASIHttpRequest:外号“HTTP终结者”,功能极其强大,可惜早已停止更新
    AFNetworking:简单易用,提供了基本够用的常用功能

ASI和AFN架构层级对比:

层级.png

说明

  1. AFN基于NSURL,ASI基于CFHTTP,ASI的性能更好一些。
  2. AFN3.0之前是通过NSURLConnection进行网络请求的,AFN3.0以后是通过NSURLSession进行网络请求的,我们主要讲述AFN3.0之后的使用。

2. AFN结构

首先看一下AFN的结构图:

结构图.png
  1. 在AFN框架中,负责网络请求通讯最重要的两个类AFURLSessionManager、AFHTTPSessionManager。
    AFURLSessionManager负责管理系统的NSURLSession和代理。
    AFHTTPSessionManager继承于AFURLSessionManager,是对外提供的接口类,我们就使用这个类做网络请求。

  2. 遵守AFURLRequestSerialization协议的类,主要用于网络请求之前的操作配置,负责配置网络请求的请求头部、序列化请求参数(请求体)。
    常用的类的继承关系如下:
    AFHTTPRequestSerializer继承于NSObject,遵守AFURLRequestSerialization协议,是基类,其他类均继承于这个类,不同的类做不同的事。

  3. 遵守AFURLResponseSerialization协议的类,主要用于网络请求之后的数据处理,针对不同的数据进行处理,比如JSON、XML、plist、图片格式等数据。
    常用的类的继承关系如下:
    AFHTTPResponseSerializer继承于NSObject,遵守AFURLResponseSerialization协议,是基类,其他类均继承于这个类,不同的类做不同的事。

  4. 附加功能类中,AFSecurityPolicy主要用于HTTPS环境下的认证安全请求通讯。
    如果是通过CA认证过的HTTPS访问地址,使用AFN时只需要拼接上https://即可,AFN的网络请求配置中默认使用CA认证访问HTTPS地址,若是自签的证书时,则需要当前类用于进行认证。

  5. AFNetworkReachabilityManager,用于网络状态的监听,判断是否有网络,以及判断网络连接类型,比如蜂窝网络或WiFi环境,但这个类无法判断当前环境能否访问服务器服务,其原理是利用主机的数据包发送。

  6. UIKit+AFNetworking,这个模块上面没写,主要是通过Category来提供了一下UIKit的便利方法。

二. AFN一次网络请求流程

AFNetworking是一个用来做网络请求的第三方库,它是对NSURLSession做网络请求的一个封装。下面我们先从一个简单的GET请求开始,从整体上把握一下AFNetworking是如何做网络请求的。

在整体阅读源码之前,我们先看一下使用NSURLSession做网络请求的步骤:

//创建Configuration对象,并设置各种属性
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.timeoutIntervalForRequest = 10.0;
configuration.allowsCellularAccess = YES;

//通过Configuration创建session,一个session可以管理多个task
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186.32812/login"];

//通过URL创建request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置request的请求方法和请求体
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];

//通过session和request来创建task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    
}];

[dataTask resume];

总结一下,使用NSURLSession进行网络请求主要分为以下几个步骤:

  1. 创建configuration对象,这个这个对象可以设置进行网络请求的一些属性,例如timeoutIntervalForRequest、allowsCellularAccess等等。
  2. 通过configuration对象创建session对象,一个session对象可以管理多个task。
  3. 通过URL创建request对象,通俗的理解,一个网络请求包括起始行,请求头,请求体,那么一个request对象也就封装了起始行,请求头,请求体这些内容。另外,其实request也可以行使configuration的功能,通过它的属性可以设置timeoutIntervalForRequest、allowsCellularAccess等等,这个后面一点我们会看到。
  4. 通过session和request来创建task对象。

下面我们使用AFNetworking来做一个网络请求:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSString *url = @"http://120.25.226.186.32812";
[manager GET:url parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {

} success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

}];

这里我们也可以看到,使用AFNetworking进行一个网络请求非常的简单,点进去:

//发送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
{

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

    //然后启动了这个task
    [dataTask resume];

    return dataTask;
}

这个很简单,创建了一个dataTask然后启动了这个task。接下来看一下dataTask是如何创建的:

//所有请求最终都会调用这个方法
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    //1.创建了一个NSMutableRequest对象
    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;
    }

    __block NSURLSessionDataTask *dataTask = nil;
    //2.使用这个NSMutableRequest对象创建了NSURLSessionDataTask对象
    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);
            }
        }
    }];

    return dataTask;
}

这里分为了两步,第一步是创建了一个NSMutableRequest对象,第二步是使用这个NSMutableRequest对象创建了NSURLSessionDataTask对象,到这里是不是就有点像用NSURLSession做网络请求呢?我们先看第一步:

1. 创建NSMutableRequest对象

- (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;
    
    //AFHTTPRequestSerializerObservedKeyPaths()方法,返回的是字符串数组
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }
    
    //设置请求头,请求体
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

我们看一下下面用KVC给mutableRequest的属性设值的部分:

//AFHTTPRequestSerializerObservedKeyPaths()方法,返回的是字符串数组
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
    if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
        [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
    }
}

看一下AFHTTPRequestSerializerObservedKeyPaths()这个方法:

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

这个方法很明显,就是把 NSMutableURLRequest的一些属性转化成字符串数组返回,然后如果self.mutableObservedChangedKeyPaths包括这个数组中的某个属性,那么就给mutableRequest对象的这个属性通过KVC设值。到这里你一定很好奇这个mutableObservedChangedKeyPaths到底是个什么东西。

我们尝试在文章开头使用AFNetworking进行网络请求那个Demo里加上下面这句:

manager.requestSerializer.allowsCellularAccess = YES;

然后在- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error这个方法里打断点,打印self.mutableObservedChangedKeyPaths的值,打印出来的结果是:

self.mutableObservedChangedKeyPaths.png

那么这就很明显了,也就是我们在外面通过requestSerializer设置的一些网络请求的属性,在内部转化到了self.mutableObservedChangedKeyPaths这个数组内部,再通过比对给request的这些属性设值。接下来我们看一下在外部给requestSerializer的一些属性设值是怎么转化到self.mutableObservedChangedKeyPaths这个数组内部的:

//手动触发了KVO
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
    [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
    _allowsCellularAccess = allowsCellularAccess;
    [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}

当我们设置manager.requestSerializer.allowsCellularAccess = YES时,代码会执行其set方法,如上,在set方法里面,很明显是手动触发了KVO,再接着看,在KVO的监听方法里:

//把key加到self.mutableObservedChangedKeyPaths数组里。
- (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];
        }
    }
}

这样就把这个key加到了self.mutableObservedChangedKeyPaths数组里了。

上面创建mutableRequest的第1步完成了,我在文章开头讲过,一个NSMutableRequest对象封装了请求的起始行,请求头,请求体,除此之外还可以设置请求的一些属性,比如allowsCellularAccess等等。那么第1步也就完成了起始行的配置,以及请求的一些属性的设置,还剩下请求头和请求体的配置,那么第2步就是为了完成这个工作:

//设置请求头,请求体
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

进入方法:

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

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    //第1步:为mutableRequest设置请求头
    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];

    NSString *query = nil;
    if (parameters) {
        //queryStringSerialization是一个block
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

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

                return nil;
            }
        } else {
            //queryStringSerializationStyle,这是一个枚举值,只有这一种情况
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    //第2步:将参数转化,并且用=,&连接起来
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    //GET,HEAD,DELETE
    //第3步:如果方法是GET,HEAD,DELETE,则直接将参数粘贴在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 {
    //第4步:如果不是以上三种方法,需要设置Content-Type首部,并把query查询语句加入body中
        // #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;
}

这里我们先看第1步:为mutableRequest设置请求头:

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

self.HTTPRequestHeaders是在AFHTTPRequestSerializer的初始化方法里面创建的,self.HTTPRequestHeaders主要包含两个请求头,即Accept-Language和User-Agent,这里就是为mutableRequest设置了这两个请求头,部分代码如下:

//给self.mutableHTTPRequestHeaders设置Accept-Language
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];

//给self.mutableHTTPRequestHeaders设置User-Agent
[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];

- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
    dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
        [self.mutableHTTPRequestHeaders setValue:value forKey:field];
    });
}

继续看第2步:将参数进行转化,并用=,&连起来,这一步的核心就是query = AFQueryStringFromParameters(parameters);这一句,我们看一下AFQueryStringFromParameters()方法的实现:

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }
//这里的逻辑就是通过AFQueryStringPairsFromDictionary()这个方法把parameters转化为一个个AFQueryStringPair对象,然后把这些对象都加入mutablePairs这个数组中,最后使用&把数组中的对象连起来,组成一个字符串。
    return [mutablePairs componentsJoinedByString:@"&"];
}

这里的逻辑就是通过AFQueryStringPairsFromDictionary()这个方法把parameters转化为一个个AFQueryStringPair对象,然后把这些对象都加入mutablePairs这个数组中,最后使用&把数组中的对象连起来,组成一个字符串。

先了解一下AFQueryStringPair这个类,这个类有两个属性,一个是field,一个是value,还有一个方法- (NSString *)URLEncodedStringValue,这个方法就是把field和value这两个属性用=连起来,组成字符串。

//这个方法就是把field和value这两个属性用=连起来,组成字符串
- (NSString *)URLEncodedStringValue {
    if (!self.value || [self.value isEqual:[NSNull null]]) {
        return AFPercentEscapedStringFromString([self.field description]);
    } else {
        return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
    }
}

接下来再来看一下AFQueryStringFromParameters()这个方法处理parameters的逻辑,这个方法的核心代码是NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value)方法,具体逻辑可自己查看源码,这个逻辑要说起来比较麻烦,这里我就用三个例子来解释一下:

  1. 当parameters是NSDictionary类型时:
id parameters = @{@"name" : @"pdd",
                  @"sex" : @"man",
                  };

返回的字符串:

name=pdd&sex=man
  1. 当parameters是NSDictionary且NSDictionary中含有NSDictionary时:
id parameters = @{@"name" : @"pdd",
                  @"sex" : @"man",
                  @"friends" : @{@"name" : @"xiaoming", @"sex" : @"man"}
                  };

返回的字符串:

name=pdd&sex=man&friends[name]=xiaoming&friends[sex]=man
  1. 当parameters是NSArray类型时:
id parameters2 = @[@"pdd", @"man"];

返回的字符串:

[]=pdd&[]=man

第2步结束了,我们再返回去,看第3步,如果方法是GET、HEAD、DELETE,则直接将第2步得到的字符串粘贴在请求URL后面。
第4步,当方法不是上面的方法时,需要为mutableRequest设置Content-Type这个请求头,并且将上面第2步得到的参数字符串作为mutableRequest的请求体。

这里为什么对于GET、HEAD、DELETE这三个方法不需要设置Content-Type这个请求头呢?这是因为根据RFC 7231的规定,只有PUT和POST请求的时候,Content-Type才是必须的。

到这里request的创建和设置就完成了,下面继续看dataTask的创建。

2. 使用NSMutableRequest对象创建NSURLSessionDataTask对象

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

    //给AFURLSessionManager设置AFURLSessionManagerTaskDelegate代理
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

url_session_manager_create_task_safely主要是解决了iOS8之前的一个系统Bug,我简单解释一下这个Bug:

在每一个NSURLSession中,每个NSURLSessionDataTask的taskIdentifier应该是独一无二的,不能和其他的NSURLSessionDataTask重叠。但是在iOS8之前,当我们并行的创建一些NSURLSessionDataTask时,这些NSURLSessionDataTask的taskIdentifier有可能重叠。
解决办法:在iOS8之前使用同步串行队列来实现他们的同步有序创建,在iOS8之后,这个Bug已经被修复。

我们看到,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();
    }
}

接下来看一下[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];方法。

这个方法非常重要,进入方法实现:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    //NSURLSessionDataTask的代理方法在AFURLSessionManager.m中实现,然后在AFURLSessionManager.m中调用AFURLSessionManagerTaskDelegate的方法
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

我们先来梳理一下AFURLSessionManager,AFURLSessionManagerTaskDelegate,NSURLSessionDataTask这三个类之间的关系:
创建session.png

上图中,在AFURLSessionManager这个类中,创建了session对象,并指定自己为session的delegate,这意味着AFURLSessionManager这个类需要实现NSURLSessionDelegate, NSURLSessionTaskDelegate ,NSURLSessionDataDelegate, NSURLSessionDownloadDelegate这些协议的方法。

实现协议方法.png

这里也确实实现了这些代理方法,但是有一些代理方法其实是在代理方法内部交给了AFURLSessionManagerTaskDelegate这个类去实现,比如下面的代理方法:

交给AFURLSessionManagerTaskDelegate.png

所以总结一下就是,AFURLSessionManager需要将NSURLSessionDataTask和AFURLSessionManagerTaskDelegate对应起来,这样在NSURLSessionDataTaask的代理方法中就可以获取到task对应的AFURLSessionManagerTaskDelegate然后交给它去处理。

NSURLSessionDataTask和AFURLSessionManagerTaskDelegate之间一一对应的关系是通过字典存储在AFURLSessionManager中的,然后通过task.taskIdentifier作为key进行存储,部分代码如下:

//存储AFURLSsssionManagerTaskDelegate的字典
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;

self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;

再回到代码中:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    //NSURLSessionDataTask的代理方法在AFURLSessionManager.m中实现,然后在AFURLSessionManager.m中调用AFURLSessionManagerTaskDelegate的方法
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    //对于上传和下载,监听上传和下载的进度并产生回调
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

总结一下AFURLSessionManagerTaskDelegate这个类的作用:

  1. 分担一部分AFURLSessionManager的工作,在一些AFURLSessionManager实现的代理方法中,直接调用了AFURLSessionManagerTaskDelegate的相关方法,这个在之前讲过,不再赘述。
  2. 对于上传和下载,监听上传和下载的进度并产生回调。
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;

添加观察者为AFURLSessionManagerTaskDelegate,如下:

//KVO监听下载和上传的进度
[progress addObserver:self
           forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
              options:NSKeyValueObservingOptionNew
              context:NULL];

AFURLSessionManagerTaskDelegate监听下载和上传的进度,进度改变,回调block:

//KVO监听下载和上传的进度,进度改变,回调block
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
   if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

AFURLSessionManagerTaskDelegate里面的方法知道进度改变了:

//进度改变
- (void)URLSession:(__unused NSURLSession *)session
          dataTask:(__unused NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
    self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;

    [self.mutableData appendData:data];
}

到这里,一个完整的GET请求就完成了。

三. AFN相关类

1. AFURLSessionManager

2. AFHTTPSessionManager

AFHTTPSessionManager是AFURLSessionManager的子类,我们在使用的时候大多数时候其实是使用的AFHTTPSessionManager,它更像是AFURLSessionManager对外的一个接口,这个类提供的API很简单:

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

- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                    parameters:(nullable id)parameters
                       success:(nullable void (^)(NSURLSessionDataTask *task))success
                       failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

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

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

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

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

以上这些方法都会统一调用AFHTTPSessionManager.m中的一个内部方法:

//所有请求最终都会调用这个方法
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    //1.创建了一个NSMutableRequest对象
    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;
    }

    __block NSURLSessionDataTask *dataTask = nil;
    //2.使用这个NSMutableRequest对象创建了NSURLSessionDataTask对象
    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);
            }
        }
    }];

    return dataTask;
}

观察上面的API也可以发现,AFHTTPSessionManager中的API都是比较简单的请求,当我们涉及到文件的上传和下载等,还是要直接使用AFURLSessionManager中的API:

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
                                          destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;

3. AFURLRequestSerialization协议

AFURLRequestSerialization是个协议,这个协议只有一个方法:

- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(nullable id)parameters
                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

AFHTTPRequestSerializer类遵循AFURLRequestSerialization协议,它有两个子类:

AFHTTPRequestSerializer.png

由于AFHTTPRequestSerializer遵循AFURLRequestSerialization协议,所以其两个子类也遵循这个协议,并实现了这个协议方法。

AFHTTPRequestSerializer这个类的作用是将request序列化,通俗的讲,就是为request设置起始行,请求头,请求体,以及一些其他的网络请求属性。

默认情况下是使用AFHTTPRequestSerializer,但是当我们为manager.requestSerializer赋值时,可使用AFJSONRequestSerializer或AFPropertyListRequestSerializer:

manager.requestSerializer = [AFJSONRequestSerializer serializer];

AFHTTPRequestSerializer、AFJSONRequestSerializer、AFPropertyListRequestSerializer三个类的不同体现在对协议方法的实现上:

AFHTTPRequestSerializer

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

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

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

    NSString *query = nil;
    if (parameters) {
        //queryStringSerialization是一个block
        if (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

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

                return nil;
            }
        } else {
            //queryStringSerializationStyle,这是一个枚举值,只有这一种情况
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    //将参数转化,并且用=,&连接起来
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    //GET,HEAD,DELETE
    //如果方法是GET,HEAD,DELETE,则直接将参数粘贴在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 {
    //如果不是以上三种方法,需要设置Content-Type首部,并把query查询语句加入body中
        // #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;
}

AFJSONRequestSerializer

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

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerializingRequest:request withParameters:parameters error:error];
    }

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

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

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }

        if (![NSJSONSerialization isValidJSONObject:parameters]) {
            if (error) {
                NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)};
                *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
            }
            return nil;
        }

        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];
        
        if (!jsonData) {
            return nil;
        }
        
        [mutableRequest setHTTPBody:jsonData];
    }

    return mutableRequest;
}

AFPropertyListRequestSerializer

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

    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerializingRequest:request withParameters:parameters error:error];
    }

    NSMutableURLRequest *mutableRequest = [request mutableCopy];

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

    if (parameters) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
        }

        NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error];
        
        if (!plistData) {
            return nil;
        }
        
        [mutableRequest setHTTPBody:plistData];
    }

    return mutableRequest;
}

self.HTTPMethodsEncodingParametersInURI定义如下:

// HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];

可以看到,当请求的方法是GET、HEAD、DELETE时,是直接调用了父类的方法的,当请求的方法不是这三个时,才会自行处理,自行处理的第一部分是为mutableRequest设置公共的请求头,第二部分是处理参数,我们以AFJSONRequestSerializer为例:

if (parameters) {
    if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
        [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    }

    if (![NSJSONSerialization isValidJSONObject:parameters]) {
        if (error) {
            NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)};
            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
        }
        return nil;
    }

    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];
    
    if (!jsonData) {
        return nil;
    }
    
    [mutableRequest setHTTPBody:jsonData];
}

首先是设置Content-Type,然后使用NSJSONSerialization对参数序列化,然后设置为request的请求体。

Content-Type.png

4. AFURLResponseSerialization协议

AFHTTPResponseSerializer类遵循AFURLResponseSerialization协议,这个协议也只有一个方法:

- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                           data:(nullable NSData *)data
                          error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

AFHTTPResponseSerializer是响应序列化的工具类,它和AFHTTPRequestSerializer很相似,有六个子类,所以都实现了如上协议方法。

AFHTTPResponseSerializer、.png

在之前的源码中,我们好像从来没有用到AFHTTPResponseSerializer及其子类,那么它是使用在哪里呢?AFHTTPResponseSerializer及其子类是用在解析从网络端获取的response。我们在AFURLSessionManager的NSURLSessionTaskDelegate这个代理中查看- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error这个方法:

//AFURLSessionManager里面的
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // delegate may be nil when completing a task in the background
    if (delegate) {
        [delegate URLSession:session task:task didCompleteWithError:error];

        [self removeDelegateForTask:task];
    }

    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

接着查看AFURLSessionManagerTaskDelegate这个类的同样的方法:
这个方法比较复杂,我们找到关键的一句:

//开始解析返回的数据,我们以AFJSONResponseSerializer为例
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

然后我们查看这个方法的实现,我们以AFJSONResponseSerializer为例:

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error
{
    //首先判断response的有效性
    if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
        if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
            return nil;
        }
    }

    // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
    // See https://github.com/rails/rails/issues/1742
    BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
    
    if (data.length == 0 || isSpace) {
        return nil;
    }
    
    NSError *serializationError = nil;
    
    //然后用NSJSONSerialization来解析NSData类型的数据
    id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];

    if (!responseObject)
    {
        if (error) {
            *error = AFErrorWithUnderlyingError(serializationError, *error);
        }
        return nil;
    }
    
    if (self.removesKeysWithNullValues) {
        return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
    }

    return responseObject;
}

这里我们看一下判断response的有效性的步骤:- (BOOL)validateResponse:(NSHTTPURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error这个方法的实现在AFJSONResponseSerializer的父类即AFHTTPResponseSerializer中:

//判断response的有效性
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
                    data:(NSData *)data
                   error:(NSError * __autoreleasing *)error
{
    BOOL responseIsValid = YES;
    NSError *validationError = nil;

    //首先判断response是否是NSHTTPURLResponse类型的
    if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
        //然后判断response的MIMEType类型是否在self.acceptableContentTypes中
        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;
        }

        //判断response的状态码是否在200-299之间,如果不在,则视为请求失败
        if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
            NSMutableDictionary *mutableUserInfo = [@{
                                               NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
                                               NSURLErrorFailingURLErrorKey:[response URL],
                                               AFNetworkingOperationFailingURLResponseErrorKey: response,
                                       } mutableCopy];

            if (data) {
                mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
            }

            validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);

            responseIsValid = NO;
        }
    }

    if (error && !responseIsValid) {
        *error = validationError;
    }

    return responseIsValid;
}

每一个AFHTTPResponseSerializer的子类在初始化时都设置了acceptableContentTypes这个属性,比如AFJSONResponseSerializer是这样初始化的:

self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

acceptableStatusCodes是在AFHTTPResponseSerializer类中初始化的:

//状态码范围是200-299
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;

即范围是200-299。

上面讲到AFHTTPResponseSerializer有六个子类,他们之间的区别其实就是根据需要把网络请求返回的数据解析成不同类型的数据,区别如下:

acceptableContentTypes.png

5. AFNetworkReachabilityManager

AFNetworkReachabilityManager这个类和前面的网络请求类没有直接的关系,我们可以看到,在前面的解读中,也从来没有提到过AFNetworkReachabilityManager这个类。这个类是用来监测设备的网络状态变化的,其使用如下:

[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    switch (status) {
        case AFNetworkReachabilityStatusUnknown:
            NSLog(@"未知网络");
            break;
        case AFNetworkReachabilityStatusNotReachable:
            NSLog(@"无网络连接");
            break;
        case AFNetworkReachabilityStatusReachableViaWWAN:
            NSLog(@"蜂窝网络");
            break;
        case AFNetworkReachabilityStatusReachableViaWiFi:
            NSLog(@"WiFi网络");
            break;
        default:
            break;
    }
}];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];

当检测到设备的网络状况改变时会调用block:

- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
    self.networkReachabilityStatusBlock = block;
}

上面的方法很简单,就是给自己的block属性赋值。

我们再看startMonitoring这个方法:

//开始检测网络
- (void)startMonitoring {
    [self stopMonitoring];

    if (!self.networkReachability) {
        return;
    }

    __weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;

        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }

    };

    SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
    SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
    SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
        SCNetworkReachabilityFlags flags;
        //检测到网络发生改变
        if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
            //回调block,发送通知
            AFPostReachabilityStatusChange(flags, callback);
        }
    });
}

看一下检测到网络发生改变之后做的事:

static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
    //获取网络状态
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
    dispatch_async(dispatch_get_main_queue(), ^{
        //回调block
        if (block) {
            block(status);
        }
        //发送通知
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
        NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
        [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
    });
}

获取网络状态:

static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
    BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
    BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
    BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
    BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
    BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));

    //通过flags得到网络状态
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
    if (isNetworkReachable == NO) {
        status = AFNetworkReachabilityStatusNotReachable;
    }
#if TARGET_OS_IPHONE
    else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
        status = AFNetworkReachabilityStatusReachableViaWWAN;
    }
#endif
    else {
        status = AFNetworkReachabilityStatusReachableViaWiFi;
    }

    return status;
}

6. AFSecurityPolicy

讲安全策略之前必须要先了解一下HTTPS的工作流程,可以看出客户端能做的就是对证书的验证,AFN内部验证的源码如下:

/*
 两种方式: 本地证书验证 (需要验证本地证书)
            1 验证服务器
            2 本地证书生成NSURLCredential对象,代理回掉block来处理相关事务
 
          无本地证书验证NSURLAuthenticationMethodServerTrust(直接信任服务器)
            1 验证服务器
            2 直接通过服务器传过来的challenge 生成NSURLCredential对象(这个对象关联了客户端https相关信息),代理回掉block来处理相关事务
 */
// 验证来源是否有效
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;
    // 两种方式 1 外部处理NSURLSessionAuthChallengeDisposition 2 AF内部默认处理它  
    if (self.sessionDidReceiveAuthenticationChallenge) {// 1外部(你自己处理)
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {// 2内部(AF处理)
        // challenge 服务器过来的,方式(NSURLAuthenticationMethodServerTrust),本地验证
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
           //自己验证  三种方式:1 无条件信任 2 证书验证  3 公约验证  返回值BOOL
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { // 验证逻辑
                // 返回YES,server是没问题的
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];// 生成一个信任的NSURLCredential对象
                if (credential) {
                    //Use the specified credential
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }

    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

可以看出,首先AFN看用户自定义验证的block有没有实现,有的话让用户自己实现,没有的话就AFN自己验证。

AFN自己验证分为三步:

  1. 获取验证策略的数据
  2. 设置策略(在evaluating中设置要使用的策略)
  3. 验证:
    ① 验证服务器是否可信
    ② 验证证书是否可信

进入AFN自己验证的代码:

 /*
 要做什么事情,然去拿什么东西(SecTrustRef)
 */
//验证服务器证书serverTrust/域名domain(服务器那边的)
/*
 1 无条件信任
 2 证书验证
 3 公钥验证
 */
// 出问题 找这个地方 返回YES,网络才没问题
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
                  forDomain:(NSString *)domain
{

    if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
        // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
        //  According to the docs, you should only trust your provided certs for evaluation.
        //  Pinned certificates are added to the trust. Without pinned certificates,
        //  there is nothing to evaluate against.
        //
        //  From Apple Docs:
        //          "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
        //           Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
        NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
        return NO;
    }
    // 1 获取验证策略的数据
    NSMutableArray *policies = [NSMutableArray array];
    if (self.validatesDomainName) { // 验证域名
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];// (SecPolicyCreateSSL 返回一个用于评估SSL证书链的策略对象)
    } else {/// 不验证域名
        //通过X.509(数字证书的标准)的数字证书和公开密钥进行的安全网络连接是否值得信任
        [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
    }
    // 2 设置策略(在evaluating中设置要使用的策略)
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    // SecTrustEvaluate();
    
    //AFSSLPinningModeNone 不校验证书
    /*
     ⚠️AFServerTrustIsValid(serverTrust) 执行这句试试,这个方法封装了SecTrustEvaluate();
     
     如果self.allowInvalidCertificates = NO;验证这个证书要有效()
      + AFServerTrustIsValid(serverTrust); 只验证域名
      那么证书过期/没有过期都一样(不验证证书是否过期,【如果服务器没有去更新证书也事没问题】)  (APP)
     
     */
    if (self.SSLPinningMode == AFSSLPinningModeNone) {
        //3 验证
        //3.1 验证服务器是否可信
        return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
    } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
        //  根据策略 验证证书是否有效 && 不允许无效证书
        return NO;
    }
    
    /*
      代码能够走到这里说明了
     1 全部信任:allowInvalidCertificates = YES 或者 域名是对的 信任
     2 通过了根证书的验证(服务器可信)(AFServerTrustIsValid)
     */
    
    // 3.2 验证证书是否可信
    switch (self.SSLPinningMode) {

        case AFSSLPinningModeCertificate: { // 全部校验
            /*
             self.pinnedCertificates
             取本地证书文件.cer,转化成NSData类型
             */
            NSMutableArray *pinnedCertificates = [NSMutableArray array];
            for (NSData *certificateData in self.pinnedCertificates) {
                [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; // 根据data生成证书对象
            }
            /*
             给serverTrust设置锚证书,即再SecTrustEvaluate评估过程中,会根据锚(pinnedCertificates)来进行验证
             */
            
            SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); //
            
            // 校验能够信任
            if (!AFServerTrustIsValid(serverTrust)) { // SecTrustEvaluate();
                return NO;
            }

            // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
            //从serverTrust评估信任的证书链中获取的证书(AFServerTrustIsValid后再次验证)
            NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
            // 判断本地证书和服务器证书是否相同
            for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
                if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
                    return YES;
                }
            }
            
            return NO;
        }
        case AFSSLPinningModePublicKey: { //公钥检验
            NSUInteger trustedPublicKeyCount = 0;
            NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);// 从证书中拿公钥数据
            // 找到相同的公钥就通过
            for (id trustChainPublicKey in publicKeys) {
                for (id pinnedPublicKey in self.pinnedPublicKeys) {
                    if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                        trustedPublicKeyCount += 1;
                        //return YES;
                    }
                }
            }
            return trustedPublicKeyCount > 0;
        }
    }
    return NO;
}

验证服务器是否可信代码:

//验证 关键方法
//返回服务器是否可信任的 本地的https版本
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
    // 这个方法会把结果赋值给result    评估/判定(根据策略,如果policy是通过domain生成的,则是域名相关policy验证)
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
    //kSecTrustResultUnspecified:不是自己设的证书的通过
    //kSecTrustResultProceed:用户自己生成证书通过
    /*
     大概意思是分两种方式:下边的自定义的意思是,用户是否是自己主动设置信任的,比如有些弹窗,用户点击了信任
     1.用户自定义的,成功是 kSecTrustResultProceed 失败是kSecTrustResultDeny
     2.非用户定义的, 成功是kSecTrustResultUnspecified 失败是kSecTrustResultRecoverableTrustFailure
     */
    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

_out:
    return isValid;
}

关于AFSSLPinningMode枚举:

AFSSLPinningModeNone: 这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。
AFSSLPinningModeCertificate:这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步,第一步验证证书的域名/有效期等信息,第二步是对比服务端返回的证书跟客户端返回的是否一致。
AFSSLPinningModePublicKey:这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。

Demo地址:AFNetworking解析

总结:

总结.png

AFHTTPSessionManager是一个管理类,在这个类的头文件中提供了进行网络请求的API,在这个类中有几个关键的属性,其作用如下

① requestSerializer

这是进行请求序列化的属性,它的默认类是AFHTTPRequestSerializer。这个属性的主要作用,就是为request设置请求的起始行,请求头,请求体,还有其他例如请求超时时间等设置。如果我们想要把请求的parameters封装为特殊的格式如Json或plist,那么我们可以使用AFHTTPRequestSerializer的子类AFJSONSerializer和AFPropertyListRequestSerializer。

② responseSerializer

这个类是进行响应序列化的属性,默认是AFJSONResponseSerializer。AFJSONResponseSerializer主要做了两个工作,第一个是验证response的有效性,验证有效性从两方面去验证,一方面是response的MIME类型是否在已设置的acceptableContentTypes,另一方面是验证response的statusCode是否在200-299范围内,两个方面有一个方面不符合则视为无效的response,不用解析,直接报错;第二个工作是对数据进行解析,从网络端得到的数据都是NSData类型的,需要对它进行转化,转化为我们需要的类型,如json,plist等。

③ session

在我们创建一个dataTask的时候,不仅需要request,还需要session对象,在AFURLSessionManager中,创建了session对象来创建dataTask。

④ reachabilityManager

这个属性创建出来是为了监听设备的网络状态使用的。

⑤ securityPolicy

在网络请求的过程中遇到认证挑战时使用这个属性。

⑥ AFURLSsssionManagerTaskDelegate

每一个dataTask都对应了一个AFURLSsssionManagerTaskDelegate对象,这种一一对应的关系以字典的形式存储在AFURLSessionManager中,如下:

@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;

在这个字典中,以 task.taskIdentifier 为key进行存储,如下:

self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;

在AFURLSsssionManagerTaskDelegate中实现了NSURLSessionDelegate、NSURLSessionTaskDelegate等协议,这其中有些协议的实现交给了AFURLSsssionManagerTaskDelegate去完成,并且AFURLSsssionManagerTaskDelegate还监听了dataTask的上传下载进度。

使用AFNetworking进行网络请求的大概过程为:

  1. 首先创建一个mutableRequest,设置其请求方法,然后根据用户的设置,设置mutableRequest的allowsCellularAccess、timeoutInterval等属性,然后设置mutableRequest的公共请求头,主要是两个:User-Agent和Accept-Language,再把请求的parameters转化为键值对,用=、&连起来组成字符串,如果请求方法是GET、HEAD、DELETE这三个方法之一,则直接把parameters字符串粘贴在请求URL后面,否则就要把parameters字符串设置成请求的请求体,并设置Content-Type请求头,到这里request相关都配置好了,并且这些配置都是在requestSerializer相关类中完成的。
  2. 创建dataTask,首先创建一个session对象,根据session对象和之前创建的request对象来创建一个dataTask,然后创建一个AFURLSessionManagerTaskDelegate对象,把这个对象和dataTask关联起来,让AFURLSessionManagerTaskDelegate对象通过KVO监听dataTask的上传下载进度,并执行相应的回调。
  3. 通过resume启动dataTask。
  4. 在请求完成的代理方法中,会使用responseSerializer来处理response,首先检查response的有效性,然后将其解析为相应的类型。

本文笔记:iOS-三方库-AFNetworking

其他参考博客:
AFNetworking源码解析与面试考点思考
AFNetworking3.0后为什么不再需要常驻线程?

推荐阅读更多精彩内容

  • 我们先看一下AFNetworking.h文件都给了我们什么方法 #import <Foundation/Found...
    潇岩阅读 406评论 0 1
  • 为什么要用AFNetworking? 1、带block形式, 内部是任务队列进行下载 ,就是对operation的...
    知本集阅读 491评论 0 2
  • AFN什么是AFN全称是AFNetworking,是对NSURLConnection、NSURLSession的一...
    醉叶惜秋阅读 724评论 0 0
  • 前言 AFNetWorking基本上是所有iOS项目的标配。现在升级带最新版的3.X了。得益于苹果从NSURLCo...
    羽裳有涯阅读 350评论 0 3
  • 1 概述 AFNetWorking基本上是所有iOS项目的标配。现在升级带最新版的3.X了。得益于苹果从NSURL...
    NS西北风阅读 6,774评论 1 21
  • 几年前跟一个朋友喝茶,开场说到,我们今天是聊赚钱还是聊梦想。 后来一致说聊梦想吧,赚钱的项目太多,我们不能见一个赚...
    简之方阅读 149评论 0 1
  • 多数人提到“年”关联到的词汇和画面已经太久没有更新过了,FYP在新的一年准备了春节特别企划系列,让大家回忆起已经被...
    不着调的小男仁阅读 115评论 0 0
  • 掐指一算,距离我和你的初见已32年。 32年前,你28岁,已是一个快5岁的娃的妈妈。 今天,是你60岁的生日。你说...
    Quinn06阅读 210评论 0 0
  • 嘿 你在哪里? 我在这里 回眸眺望了千百次 倦了 你在哪里 我还在这里 嘿 你呀你 不要在别的女人的温柔乡呆太久 ...
    迷雾小黑阅读 143评论 3 3