美文网首页
优秀iOS三方框架解析一(AFNetWorking)

优秀iOS三方框架解析一(AFNetWorking)

作者: 懒懒的猫 | 来源:发表于2023-03-01 17:08 被阅读0次

    一、结构

    AFNetworking是iOS最常用的网络框架,虽然系统也有NSURLSession,但是我们一般不会直接用它。AFNetworking经过了三个大版本,现在用的大多数都是3.x的版本。

    AFNetworking3.X的构成很简单,主要就四部分,除此之外还有一些基于UIKitCategory

    • Manager : 负责处理网络请求的两个Manager,主要实现都在AFURLSessionManager中。
    • Reachability : 网络状态监控。
    • Security : 处理网络安全和HTTPS相关的。
    • Serialization : 请求和返回数据的格式化器。
    image.png

    二、核心逻辑

    先来看一下如何使用 AFNetworking 发送一个 请求:

    NSURL *url = [[NSURL alloc] initWithString:@"https://news-at.zhihu.com"];
    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
    [manager GET:@"api/4/news/latest" parameters:nil progress:nil
        success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"%@" ,responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"%@", error);
        }];
    

    首先使用一个 URL,通过调用 -initWithBaseURL:方法创建了一个 AFHTTPSessionManager 的实例,然后再调用-GET:parameters:progress:success:failure: 方法发起请求。

    -initWithBaseURL: 方法的调用栈如下:

    - [AFHTTPSessionManager initWithBaseURL:]
        - [AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]
            - [AFURLSessionManager initWithSessionConfiguration:]
                - [NSURLSession sessionWithConfiguration:delegate:delegateQueue:]
                - [AFJSONResponseSerializer serializer] // 负责序列化响应
                - [AFSecurityPolicy defaultPolicy] // 负责身份认证
                - [AFNetworkReachabilityManager sharedManager] // 查看网络连接情况
                - [AFHTTPRequestSerializer serializer] // 负责序列化请求
                - [AFJSONResponseSerializer serializer] // 负责序列化响应
    
    

    AFURLSessionManagerAFHTTPSessionManager 的父类, AFURLSessionManager 负责创建和管理 NSURLSession 的实例,管理 AFSecurityPolicy 和初始化 AFNetworkReachabilityManager,来保证请求的安全和查看网络连接情况,它有一个 AFJSONResponseSerializer 的实例来序列化 HTTP 响应。
    AFHTTPSessionManager 有着自己的 AFHTTPRequestSerializerAFJSONResponseSerializer 来管理请求和响应的序列化,同时依赖父类实现发出 HTTP 请求、管理 Session 这一核心功能。

    -GET:parameters:progress:success:failure:方法的调用栈:

     - [AFHTTPSessionManager GET:parameters:process:success:failure:]
        - [AFHTTPSessionManager dataTaskWithHTTPMethod:parameters:uploadProgress:downloadProgress:success:failure:] // 返回一个 NSURLSessionDataTask 对象
            - [AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:] // 返回 NSMutableURLRequest
            - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:] 
                - [NSURLSession dataTaskWithRequest:]// 返回一个 NSURLSessionDataTask 对象
                - [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:]
                    - [AFURLSessionManagerTaskDelegate init]
                    - [AFURLSessionManager setDelegate:forTask:] // 为每个 task 创建一个对应的 delegate
        - [NSURLSessionDataTask resume]
    

    发送请求的核心在于创建和启动一个 data task,AFHTTPSessionManager 只是提供了 HTTP 请求的接口,内部最终还是调用了父类 AFURLSessionManager 来创建 data task(其实也就是通过 NSURLSession 创建的 task),AFURLSessionManager 中会为每个 task 创建一个对应的 AFURLSessionManagerTaskDelegate 对象,用来处理回调。
    在请求发起时有一个序列化的工具类 AFHTTPRequestSerializer 来处理请求参数。

    请求回调时的方法调用栈:

    - [AFURLSessionManager  URLSession:task:didCompleteWithError:]
      - [AFURLSessionManagerTaskDelegate URLSession:task:didCompleteWithError:]
        - [AFJSONResponseSerializer responseObjectForResponse:data:error:]  // 解析 JSON 数据
          - [AFHTTPResponseSerializer validateResponse:data:]  // 验证数据
        - [AFURLSessionManagerTaskDelegate URLSession:task:didCompleteWithError:]_block_invoke_2.150
          - [AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure:]_block_invoke
    

    AFURLSessionManager 在代理方法中收到服务器返回数据的后,会交给 AFURLSessionManagerTaskDelegate 去处理,接着就是用 AFJSONResponseSerializer 去验证和解析 JSON 数据,最后再通过 block 回调的方式返回最终结果。

    三、AFURLSessionManager

    AFURLSessionManager 是 AFHTTPSessionManager 的父类,主要有以下几个功能:

    • 负责创建和管理 NSURLSession
    • 管理 NSURLSessionTask
    • 实现 NSURLSessionDelegate 等协议中的代理方法
    • 使用 AFURLSessionManagerTaskDelegate 管理上传、下载进度,以及请求完成的回调
    • 将整个请求流程相关的组件串联起来
    • 负责整个请求过程的线程调度
    • 使用 AFSecurityPolicy 验证 HTTPS 请求的证书
    1. 线程

    一般调用 AFNetworking 的请求 API 时,都是在主线程,也是主队列。然后直到调用 NSURLSession 的 -resume 方法,一直都是在主线程。
    在 AFURLSessionManager 的初始化方法中,设置了 NSURLSession 代理回调线程的最大并发数为 1,因为就像 NSURLSession 的 -sessionWithConfiguration:delegate:delegateQueue: 方法的官方文档中所说的那样,所有的代理方法回调都应该在一个串行队列中,因为只有这样才能保证代理方法的回调顺序。
    NSURLSession 代理方法回调是异步的,所以收到回调时的线程模式是“异步+串行队列”,这个时候可以理解为处于回调线程。
    收到代理回调后,接着在 AFURLSessionManagerTaskDelegate 的 -URLSession:task:didCompleteWithError: 方法中,异步切换到 processing queue 进行数据解析,数据解析完成后再异步回到主队列或者自定义队列。

    image.png
    2. AFURLSessionManagerTaskDelegate

    AFURLSessionManager 中几乎实现了所有的 NSURLSession 相关的协议方法

    • NSURLSessionDelegate
    • NSURLSessionTaskDelegate
    • NSURLSessionDataDelegate
    • NSURLSessionDownloadDelegate

    但是AFURLSessionManager 中实现的这些代理方法都只是做一些非核心逻辑的处理,每个代理方法中都回调了一个自定义逻辑的 block,如果 block 被赋值了,那么就调用它。
    AFURLSessionManager 把最核心的代理回调处理交给 AFURLSessionManagerTaskDelegate 类去实现了,AFURLSessionManagerTaskDelegate 可以根据对应的 task 去进行上传、下载进度回调和请求完成的回调处理:

    - URLSession:task:didCompleteWithError:
    - URLSession:dataTask:didReceiveData:
    - URLSession:downloadTask:didFinishDownloadingToURL:
    

    AFURLSessionManager 通过属性 mutableTaskDelegatesKeyedByTaskIdentifier (一个 NSDictionary 对象)来存储并管理每一个 NSURLSessionTask 所对应的 AFURLSessionManagerTaskDelegate,它以 taskIdentifier 为键存储 task。在请求最终完成后,又将 AFURLSessionManagerTaskDelegate 移除。


    image.png
    3. NSProgress

    AFURLSessionManagerTaskDelegate 借助了 NSProgress 这个类来实现进度的管理,NSProgress 是 iOS 7 引进的一个用来管理任务进度的类,可以表示一个任务的进度信息,我们还可以对其进行开始 暂停、取消等操作,完整的对应了 task 的各种状态。
    AFURLSessionManagerTaskDelegate 通过 KVO 监听 task 的进度更新,来同步更新 NSProgress 的进度数据。同时,还用 KVO 监听了 NSProgress 的 fractionCompleted 属性的变化,用来更新最外面的进度回调 block,回调时将这个 NSProgress 对象作为参数带过去。
    另一方面,AFURLSessionManagerTaskDelegate 中还分别对下载和上传的 NSProgress 对象设置了开始、暂停、取消等操作的 handler,将 task 跟 NSProgress 的状态关联起来。这样一来,就可以通过控制 NSProgress 对象的这些操作就可以控制 task 的状态。

    四.AF2.0 与 3.0的不同

    4.1 AFNetworking3.0为何弃用了NSURLConnection
    NSURLSession提升了网络连接速度

    2015年,RFC文档(RFC文档是一系列关于Internet的技术资料汇编,早期为ARPANET)编号7540正式发表了下一代HTTP协议HTTP/2,是1999年发布HTTP/1.1以来的首个更新。相对于前一个版本,HTTP/2以快著称。加载同样一张图片,新的协议所用时间是旧协议的四分之一。
    根据2015的WWDC Session711,我们知道iOS9+,NSURLSession开始正式支持HTTP/2,也就意味着你的网络连接速度可以提升不少。更人性化更优秀的API设计,HTTP/2的支持,成为了开发者摒弃NSURLConnection的理由。

    Session采用了共享,而非每次都新建

    事实上在HTTP/0.9 ,HTTP/1.0协议的时代,每次HTTP的请求,都需要先经过TCP的连接,而后才能开始HTTP的请求。那么,为了让我们的请求更快,避免每次都产生一个TCP三次握手,成了一个优化的选项。于是在HTTP/1.1中共享的Session将会复用TCP的连接,这样就避免了每次操作都开启一个TCP三次握手的时间浪费,即加速了网络请求时间。
    通过查看文档,我们可以知道iOS对同一个IP的服务器的最大并发数为4,OSX为6,但是如果没有共享Session,则可能会超过这个数。

    4.2 AFNetworking3.0后为什么不再需要常驻线程?
    AF2.x为什么需要常驻线程?

    开辟一条子线程,设置runloop使线程常驻。所有的请求在这个线程上发起、同时也在这个线程上回调。

    首先,每一个请求对应一个AFHTTPRequestOperation实例对象(以下简称operation),每一个operation在初始化完成后都会被添加到一个NSOperationQueue中。
    由这个NSOperationQueue来控制并发,系统会根据当前可用的核心数以及负载情况动态地调整最大的并发 operation 数量,我们也可以通过setMaxConcurrentoperationCount:方法来设置最大并发数。注意:并发数并不等于所开辟的线程数。具体开辟几条线程由系统决定。
    也就是说此处执行operation是并发的、多线程的。

    image.png
    最后再来小结一下为什么AF2.x需要一条常驻线程:
    首先需要在子线程去start connection,请求发送后,所在的子线程需要保活以保证正常接收到 NSURLConnectionDelegate 回调方法。如果每来一个请求就开一条线程,并且保活线程,这样开销太大了。所以只需要保活一条固定的线程,在这个线程里发起请求、接收回调。
    AF3.x为什么不再需要常驻线程?

    NSURLConnection的一大痛点就是:发起请求后,这条线程并不能随风而去,而需要一直处于等待回调的状态。

    苹果也是明白了这一痛点,从iOS9.0开始 deprecated 了NSURLConnection。 替代方案就是NSURLSession。

    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    

    同时还要注意一下,指定的用于接收回调的Queue的maxConcurrentOperationCount设为了1,这里目的是想要让并发的请求串行的进行回调。

    为什么要串行回调?

    - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
        NSParameterAssert(task);
        AFURLSessionManagerTaskDelegate *delegate = nil;
        [self.lock lock];
        //给所要访问的资源加锁,防止造成数据混乱
        delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
        [self.lock unlock];
        return delegate;
    }
    

    这边对 self.mutableTaskDelegatesKeyedByTaskIdentifier 的访问进行了加锁,目的是保证多线程环境下的数据安全。既然加了锁,就算maxConcurrentOperationCount不设为1,当某个请求正在回调时,下一个请求还是得等待一直到上个请求获取完所要的资源后解锁,所以这边并发回调也是没有意义的。相反多task回调导致的多线程并发,还会导致性能的浪费。

    所以,为什么在AF3.0中。要设置 self.operationQueue.maxConcurrentOperationCount = 1; 呢?AF2.0却不需要?
    解答:功能不一样:AF3.0的operationQueue是用来接收NSURLSessionDelegate回调的,鉴于一些多线程数据访问的安全性考虑,设置了maxConcurrentOperationCount = 1来达到串行回调的效果。
    而AF2.0的operationQueue是用来添加operation并进行并发请求的,所以不要设置为1。

    相关文章

      网友评论

          本文标题:优秀iOS三方框架解析一(AFNetWorking)

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