美文网首页源码解读
AFNetWorking 源码学习笔记 ☞ NSURLSessi

AFNetWorking 源码学习笔记 ☞ NSURLSessi

作者: RiverSea | 来源:发表于2018-11-20 18:46 被阅读47次
    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

    相关文章

      网友评论

        本文标题:AFNetWorking 源码学习笔记 ☞ NSURLSessi

        本文链接:https://www.haomeiwen.com/subject/mnlofqtx.html