AFNetWorking 源码学习笔记 ☞ NSURLSession

AFNetWorking 源码学习笔记.png

一、前言

本文是 AFNetWorking 源码学习笔记的第二篇,从本篇开始每次介绍四个部分中的一个,按顺序先介绍第一部分 -- NSURLSession 目录下的两个关键类 AFHTTPSessionManager 和 AFURLSessionManager。

注:标题容易让人误解,其实指的是 AFNetWorking 源码中的 NSURLSession 目录,而不是真的 NSURLSession 这个类。

AFNetWorking-NSURLSession.png

二、正文

我们从整个框架对外的最直接接口 AFHTTPSessionManager.h 文件开始查看,此文件主要分三部分:

第一部分:4个基本属性:

①baseURL:顾名思义,基础的 URL ,对于同一个 App,通常情况下,请求 url 的前半部分都是相同的,将这一部分赋值给 baseURL,发送请求时就可以使用相对路径了。
②requestSerializer:请求的序列化类,负责拼接参数,及对参数的编码。
③responseSerializer:负责对请求结果的处理,
④securityPolicy:安全策略类,负责对服务器证书的内部验证,即在 AFNetWorking 内部先进行一次验证。

第二部分:创建及初始化方法

虽然提供了3种看似不同的创建及初始化方法(见下方代码),实际上前两者只是对后者不同程度的封装,最终都是调用了最后一个参数最多的初始化方法。

AFHTTPSessionManager.h

// 并非单例,从方法名就可以看出来
+ (instancetype)manager;

- (instancetype)initWithBaseURL:(nullable NSURL *)url;

- (instancetype)initWithBaseURL:(nullable NSURL *)url
           sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

AFHTTPSessionManager.m

// *** 初始化 最终都调用的是这个方法
- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    // 0.调用父类初始化方法
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

    // 1.调整 url, 确保 NSURL 的 +URLWithString:relativeToURL:  方法能够正常调用
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }

    // 2.初始化本类的属性
    self.baseURL = url;
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;
}

第一步就是调用父类(AFURLSessionManager)的 initWithSessionConfiguration: 方法初始化,然后才是初始化自己的属性,点开父类方法看看。

// *** init 子类初始化时会通过 super 调用😎
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    // 初始化 configuration,如果为空,则使用默认,即 defaultSessionConfiguration,默认配置使用的是持久化的硬盘缓存,存储证书到用户 keychain,存储 cookie 到 shareCookie。
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    self.sessionConfiguration = configuration;

    // 创建 delegate 所在的操作队列,因为 maxConcurrentOperationCount = 1,所以此处是串行队列,如果 > 1,则为并发队列。
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    /**
     *  初始化 session
     *  用于后边创建各种 task,这里设置了 delegate、operationQueue 和 sessionConfiguration
     *  注意1: 这个 delegate 是 readonly,只能通过初始化方法设置;
     *  注意2: self.operationQueue 默认是串行队列;
     *  注意3: self.sessionConfiguration 默认取 defaultSessionConfiguration。
     */
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    // 用于对接口返回结果的处理 - 下一篇详细说明
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    // 设置默认的安全策略类
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

    // 如果是 Apple Watch,则不需要初始化 reachabilityManager
#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif

    // 存放 task.idenfier 与其 delegate 组成的键值对,用于将系统提供的代理方法转发给其 delegate
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    // 初始化锁 🔐
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

    /**
     *  理论上,在初始化的时候,应该是还没有创建 task 的。
     *
     *  一个代码贡献者给出的解释是 I believe this for restoring a session from the background. 即 为从后台恢复一个 session 的情况准备的,将其代理的所有回调全都置为 nil。
     *  详见:https://github.com/AFNetworking/AFNetworking/issues/3499
     */
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }

        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }

        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}
第三部分:核心 - 发送请求的方法

细数 AFHTTPSessionManager 中提供的一系列发送请求的方法,可以发现,其实主要可以分为两大类:一类是普通的 GET/POST/HEAD/PUT/PATCH/DELETE 方法,另一类是包含复合结构 request 的 POST 请求方法。

第一类方法

下面我们首先从第一类方法开始讨论,主要有以下几个方法,此处做了简化:

- (nullable NSURLSessionDataTask *)GET: parameters: success: failure: DEPRECATED_ATTRIBUTE

- (nullable NSURLSessionDataTask *)GET: parameters: progress: success: failure:

- (nullable NSURLSessionDataTask *)HEAD: parameters: success: failure:

- (nullable NSURLSessionDataTask *)POST: parameters: success: failure: DEPRECATED_ATTRIBUTE

- (nullable NSURLSessionDataTask *)POST: parameters: progress: success: failure:

- (nullable NSURLSessionDataTask *)PUT: parameters: success: failure:

- (nullable NSURLSessionDataTask *)PATCH: parameters: success: failure:

- (nullable NSURLSessionDataTask *)DELETE: parameters: success: failure:

这些方法的实现类似,都是调用了同一个方法创建 task,只不过传入的 method 不同而已,然后 resume 这个 task。如下边的这个 GET 方法所示,其他方法,只是将 GET 换成了对应 POST、HEAD 等等。

- (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
{
    // 1.创建任务
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    // 2.启动任务
    [dataTask resume];

    return dataTask;
}

这个创建方法 dataTaskWithHTTPMethod: ... 最终做了 2 件事:

// *** 构建 NSURLSessionDataTask
- (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
{
    // 1. 构建 NSMutableURLRequest,实际调用 requestSerializer 中创建 request 的方法
    // 因为 NSURLRequest 的属性都是 readonly,所以此处构建了 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;
    }
    
    // 2. 构建 NSURLSessionDataTask:实际调用父类 AFURLSessionManager 的方法
    __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);
                               }
                           }
                       }];
    
    return dataTask;
}

1.调用 AFHTTPRequestSerializer 的 requestWithMethod: URLString: parameters: error: 方法创建 mutableURLRequest;

2.利用 父类即 AFURLSessionManager 的 dataTaskWithRequest: uploadProgress: downloadProgress: completionHandler: 创建 task(需要上一步得到的 mutableURLRequest 做参数)。

AFHTTPRequestSerializer 中的方法主要做了一些拼接参数及编码的工作,留待下一篇介绍。这里来看看 AFURLSessionManager 中的方法:

- (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 {

    // 1.创建Task(同时修复iOS8以下系统出现的Bug)
    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    // 2.为 task 添加代理
[self addDelegateForDataTask:dataTask
              uploadProgress:uploadProgressBlock
            downloadProgress:downloadProgressBlock
           completionHandler:completionHandler];

    return dataTask;
}

这里也做了 2 件事,见上边注释,不过,创建 dataTask 的时候使用了一个 block,点开定义发现,当 iOS 系统版本 < 8.0 时,创建了一个串行队列来执行 block 中的任务。

这是为了防止 iOS8 以前的版本在并发队列上创建任务时,可能会调用错误的 completionHandlers。当任务返回一个重复的 taskIdentifier 时,先前的 completionHandler 被清除并替换为新的。 如果第一个请求的数据在第二个请求的数据之前返回,那么将针对第二个 completionHandler 调用第一个响应。

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

static dispatch_queue_t url_session_manager_processing_queue() {
    static dispatch_queue_t af_url_session_manager_processing_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
    });

    return af_url_session_manager_processing_queue;
}
第二类方法

第二类方法最终调用的只有一个方法,如下所示:

- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(id)parameters
     constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                      progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
    // 1.创建 request
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
    if (serializationError) {
        if (failure) {
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }

        return nil;
    }

    // 2.创建 task
    __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(task, error);
            }
        } else {
            if (success) {
                success(task, responseObject);
            }
        }
    }];

    // 3.启动任务
    [task resume];

    return task;
}

基本步骤与第一类方法类似,只不过创建 request 和 task 的方法不一样,对于 request 的创建方法留待下一篇介绍,这里我们看看创建 dataTask 的方法有什么不同。

- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                 progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                        completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionUploadTask *uploadTask = nil;
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithStreamedRequest:request];
    });

    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];

    return uploadTask;
}

原来是其中创建 task 的方法 uploadTaskWithStreamedRequest: 不同,生成的是 NSURLSessionUploadTask 类型,之后设置代理的方法与第一类方法类似。

注意:其实理论上应该还有第 3 类请求方法,即下载的请求,不过 AFHTTPSessionManager 里边并未封装,而只是在它的父类里边提供了创建 downloadTask 的方法,比如下边的。也是创建 task 使用的系统方法不同,其他类似。

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                          destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    __block NSURLSessionDownloadTask *downloadTask = nil;
    url_session_manager_create_task_safely(^{
        downloadTask = [self.session downloadTaskWithRequest:request];
    });

    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];

    return downloadTask;
}

现在分别介绍完了 2 类发送请求的方法,下边我们看看为 task 添加代理的方法。

- (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
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    // 保存 task 和 delegate 的映射关系,并添加对任务开始和暂停的监听
    [self setDelegate:delegate forTask:dataTask];

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

这里创建了一个代理对象(AFURLSessionManagerTaskDelegate),在这个 task 中对请求过程进行处理;设置完代理的属性之后,才调用 setDelegate: forTask: 方法来为 task 及代理添加关联。

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    // 以 taskIdentifier 为 key,将 delegate 存储在一个字典(self.mutableTaskDelegatesKeyedByTaskIdentifier)里
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    // 添加对任务开始和暂停的监听
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

主要就是保存 task 和 代理之间的映射关系,保存在之前已经创建好的字典 mutableTaskDelegatesKeyedByTaskIdentifier 里边,并为 task 添加通知,监听了 task resume 和 suspend 两个状态。然后我们发现当状态发生改变的时候,实际上只是在主队列又发送了 2 中通知,只是改了个名字而已。全局搜索新的通知名后发现,只有在 UIKit 那个目录下的类里边才会用到。

然后看看代理类 AFURLSessionManagerTaskDelegate,从他的初始化方法开始。

- (instancetype)initWithTask:(NSURLSessionTask *)task {
    self = [super init];
    if (!self) {
        return nil;
    }
    
    // 1.存放下载的数据
    _mutableData = [NSMutableData data];
    
    //  2.创建并初始化上传及下载进度 _uploadProgress 和 _downloadProgress
   _uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
    _downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
    
    __weak __typeof__(task) weakTask = task;
    for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
    {
        progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
        
        progress.cancellable = YES; // 可以取消
        progress.cancellationHandler = ^{
            [weakTask cancel];
        };
        
        progress.pausable = YES; // 可以暂停
        progress.pausingHandler = ^{
            [weakTask suspend];
        };
        
        // 恢复
#if AF_CAN_USE_AT_AVAILABLE
        if (@available(iOS 9, macOS 10.11, *))
#else
        if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
        {
            progress.resumingHandler = ^{
                [weakTask resume];
            };
        }
        
        // 给 progress 添加监听
        [progress addObserver:self
                   forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                      options:NSKeyValueObservingOptionNew
                      context:NULL];
    }
    return self;
}

从上边的代码可以看到,就做了 2 件事:

  • 创建存放下载的数据的可变数组 _mutableData;
  • 创建用于存储上传及下载进度的对象 _uploadProgress 和 _downloadProgress,并为其添加 KVO 监听。
    查看了 KVO 对应的 observeValueForKeyPath: 方法实现后,发现只是将最新的下载或上传进度回传给了对应的 block。当然,会在 dealloc 方法中移除监听,否则会 Crash。
- (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);
        }
    }
}

接下来就是一些代理方法了,他们都是从 AFURLSessionManager 里边的代理方法转发过来的,后边再作介绍吧。

回到 AFURLSessionManager.m 中,前边都是在请求发送之前做的准备工作,现在我们来看看发起请求之后的情况,当然是看代理方法了。

代理方法

这里的 15 个代理方法分属 4 个不同的协议:
NSURLSessionDelegate
NSURLSessionTaskDelegate
NSURLSessionDataDelegate
NSURLSessionDownloadDelegate

我们依次看看其中实现的协议方法:

NSURLSessionDelegate
// 当 session 失效时调用
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
    if (self.sessionDidBecomeInvalid) {
        self.sessionDidBecomeInvalid(session, error);
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}

// 如何接受授权挑战,如果此方法未实现,会调用后边 NSURLSessionTaskDelegate 中的第一个代理方法 URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;

    //  1.判断有没有自定义 block: sessionDidReceiveAuthenticationChallenge
    if (self.sessionDidReceiveAuthenticationChallenge) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
        
        //  2.如果没有自定义 block,判断 如果服务端要求的认证方法是不是 NSURLAuthenticationMethodServerTrust
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            
            //  3.基于客户端的安全策略来决定是否信任该服务器
            // *** 点开此方法
            if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential; // 使用证书的 Challenge
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling; // 默认 Challenge
                }
                
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; // 取消 Challenge
            }
            
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling; // 默认 Challenge
        }
    }

    // *** 去执行 iOS 系统的认证方案
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

对于第一个方法 URLSession: didBecomeInvalidWithError:,当 session 失效时调用此方法。官方文档给出的具体说明是,如果我们调用了 finishTasksAndInvalidate 方法,则会等到 session 里边最后一个 task 执行完,才会调用此代理方法;如果调用了 invalidateAndCancel 这个方法,则会立即执行该代理方法。

第 2 个代理方法是当客户端收到 “授权挑战”(姑且这么翻译吧,虽然很别扭) 时实行的方法,流程如下图:

AFNetWorking认证挑战.png
  • 首先,判断用户(使用者)有没有自定义 block: sessionDidReceiveAuthenticationChallenge,如果有,则执行自定义的 block 处理挑战,返回一个枚举值给 disposition,并给 credential 赋值 (因为参数是 &credential) 处理,他们都将作为最后一步执行 completionhandler() 时的参数。如果没有自定义处理挑战的 block,则接着进行下一步的判断;

  • 然后,判断服务端要求的认证方法是不是 NSURLAuthenticationMethodServerTrust。如果是,则只需要验证服务端证书是否安全(即 https 的单向认证,这是 AFNetworking 默认处理的认证方式,其他的认证方式,只能通过定义 block 来实现)。这种情况下,还需要做下一步处理;

  • 接下来,有一个 if 判断,条件是 securityPolicy 调用 evaluateServerTrust: forDomain: 方法的执行结果,该方法实际是 AFNetWorking 自己对先做的一次 HTTPS 认证,返回结果表明该服务器是否可以信任,具体方法实现见下一篇

    • 如果 if 条件成立,即 AFNetworking 认为服务器可信任,执行 NSURLCredential 的系统方法 credentialForTrust: 生成一个凭证或称证书 credential,如果成功生成,就给 disposition 赋相应的值。
    • 如果 if 条件不成立,则取消认证。
  • 最后,执行 completionHandler(disposition, credential),其中,credential 是根据服务端返回的证书及相关信息生成的用于客户端验证的对象;disposition 就是一个枚举值(常量),用于说明应该以何种方式处理凭证 credential

NSURLSessionTaskDelegate

这里以典型的请求结束的代理方法为例做一个简单介绍,代码如下:

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    // 取出 task 对应的 delegate
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // 如果是在后台完成的任务,则此处 delegate 为 nil。
    if (delegate) {
        [delegate URLSession:session task:task didCompleteWithError:error];
        [self removeDelegateForTask:task];
    }

    // 执行 block
    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

这是在请求结束时执行的代理方法(可能成功或者失败,如果失败,error有值),调用自己的 delegateForTask: 方法,取出 task 对应的 delegate,如果取到了,则执行 delegate 的 URLSession: task: didCompleteWithError: 方法。

注意,实际我们最开始传到 GET、POST 等方法中的 block 都是赋值给了对应 task 的 delegate,所以真正执行那些 block 也是在转发到的 delegate(AFURLSessionManagerTaskDelegate) 的对应代理方法中,而不是上边代码最后的 block。

- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;

    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    //Performance Improvement from #2672
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        //We no longer need the reference, so nil it out to gain back some memory.
        self.mutableData = nil;
    }

    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    if (error) {    // 出错的时候
        
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

        // 出错时,执行完成的回调 及 发送任务结束的通知
        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{

            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
        
    } else {    // 正常结束的时候
        
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            
            // *** 使用 responseSerializer 处理返回结果
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }

            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }

            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }

            // 正常结束的时候,执行完成的回调 及 发送任务结束的通知
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}

仔细看了一下,这个方法里边就主要就为了做了 2 件事,第一件是执行 self.completionHandler(),包括请求结果的序列化将会在下一篇介绍;第二件是发送任务完成的通知,包括构建 userInfo 这个可变字典,详见代码。

至于其他协议及代理方法,大部分都是遵循将代理方法转发给 AFURLSessionManagerTaskDelegate 里的对应方法的逻辑,限于篇幅暂时就不做讨论了。

彩蛋

忽然想起来,其实 AFNetWorking 的 readme.md 里边给的 4 个示例都是直接使用 AFURLSessionManager 而不是使用它的子类 AFHTTPSessionManager,难道我又说多了,哈哈。

参考

NS_UNAVAILABLE 与 NS_DESIGNATED_INITIALIZER
进度: NSProgress

推荐阅读更多精彩内容