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

2023-03-01  本文已影响0人  懒懒的猫

一、结构

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

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

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 的父类,主要有以下几个功能:

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 相关的协议方法

但是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。

上一篇下一篇

猜你喜欢

热点阅读