AFURLSessionManager 和 AFHTTPSessionManager 是AFNetworking的核心类
这里先阅读AFURLSessionManager。
使用 AFURLSessionManager 创建下载任务
1 | NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; |
AFURLSessionManager 初始化
initWithSessionConfiguration:
创建和管理 NSURLSession
1 | - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration{ |
1. 初始化相应的属性
- 初始化会话配置(NSURLSessionConfiguration),默认为
defaultSessionConfiguration
- 初始化会话(NSURLSession),设置会话的配置,代理以及队列
- 初始化响应序列化(AFJSONResponseSerializer),安全认证(AFSecurityPolicy),网络状态监控(AFNetworkReachabilityManager)
- 初始化保存 data task 的字典(mutableTaskDelegatesKeyedByTaskIdentifier)
2. 为task设置代理
使用getTasksWithCompletionHandler
从session中取出task,并为task设置代理。
管理NSURLSessionTask
AFURLSessionManager初始化后,就可以获取NSURLSessionTask对象
AFNetworking3 中有7个方法分别返回NSURLSessionDataTask
,NSURLSessionUploadTask
,NSURLSessionDownloadTask
。这里从以下方法来看是如何实现的:
1 | - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request |
其中 url_session_manager_create_task_safely
是解决iOS8之前的一个bug ,详情可以看 Issue 2093
- 通过传入的NSURLRequest生成NSURLSessionUploadTask对象
- 调用自定义方法
addDelegateForUploadTask...
来为task添加回调事件
1 | - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask |
- 在这个方法中 AFURLSessionManagerTaskDelegate 类实现了
NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate
代理。 - 使用
setDelegate
来设置代理
1 | - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate |
AFURLSessionManager 就是通过字典 mutableTaskDelegatesKeyedByTaskIdentifier 来存储并管理每一个 NSURLSessionTask,它以 taskIdentifier 为键存储 task。
该方法使用 NSLock 来保证不同线程使用 mutableTaskDelegatesKeyedByTaskIdentifier 时,不会出现线程竞争的问题。
实现 NSURLSessionDelegate
AFURLSessionManager初始化的时候调用 initWithSessionConfiguration,将 NSURLSession 的代理设置为self
AFURLSessionManager实现了这些协议:
- NSURLSessionDelegate
- NSURLSessionTaskDelegate
- NSURLSessionDataDelegate
- NSURLSessionDownloadDelegate
所有的delegate 实现方法都提供对应的block接口,比如:
1 |
|
对应的block接口是:
1 | - (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block; |
使用 AFURLSessionManagerTaskDelegate 管理进度
上文的AFURLSessionManagerTaskDelegate实现了
NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate
代理。
它主要为 task 提供进度管理功能,并在 task 结束时回调,这里开始查看如何实现的。
1 | - (instancetype)initWithTask:(NSURLSessionTask *)task { |
1. 设置上传或者下载任务状态改变的是回调
1 | progress.totalUnitCount = NSURLSessionTransferSizeUnknown; |
通过对应的NSProgress状态改变时,调用 resume
suspend
等方法改变 task 的状态
2. KVO
1 | [progress addObserver:self |
1 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { |
对progress进行键值观察,对象的某些属性改变时更新 NSProgress 对象或使用 block 传递 NSProgress 对象 self.uploadProgressBlock(object)
或者 self.downloadProgressBlock(object);
。其中fractionCompleted表示 NSProgress的进度。
代理方法 URLSession:task:didCompleteWithError:
当task完成数据传输时会调用这个方法,接下来看一下在 AFURLSessionManagerTaskDelegate中的实现
1 | - (void)URLSession:(__unused NSURLSession *)session |
1. 取出数据。储存responseSerializer downloadFileURL
1 | __strong AFURLSessionManager *manager = self.manager; |
这部分代码从 mutableData 中取出了数据,设置了 userInfo。其中 self. mutableData
是从URLSession:dataTask:didReceiveData
代理方法中获取的。userInfo 用来做通知时传送的数据
2. 3.调用completionHandler
该代理方法中的error仅指本地端的错误,比如无法解析域名或者不能连上host等。
1 | dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ |
如果当前 manager 持有 completionGroup 或者 completionQueue 就使用它们。否则会创建一个 dispatch_group_t 并在主线程中调用 completionHandler 并发送通知(在主线程中)。
1 | NSError *serializationError = nil; |
如果没有error,先对数据进行序列化,然后同样调用 block 并发送通知。
_AFURLSessionTaskSwizzling
因为 iOS7 和 iOS8 上对于 NSURLSessionTask 的实现不同,AFURLSessionTaskSwizzling 的就是修改 NSURLSessionTask 的 resume 和 suspend 方法
使用+load 替换方法
1 | +(void)load{ |
- 首先用 NSClassFromString(@”NSURLSessionTask”) 判断当前部署的 iOS 版本是否含有类 NSURLSessionTask
- 因为 iOS7 和 iOS8 上对于 NSURLSessionTask 的实现不同,所以会通过 - [NSURLSession dataTaskWithURL:] 方法返回一个 NSURLSessionTask 实例
- 取得当前类 _AFURLSessionTaskSwizzling 中的实现 af_resume
- 如果当前类 currentClass 有 resume 方法
- 真:5
- 假:6
- 使用 swizzleResumeAndSuspendMethodForClass: 调剂该类的 resume 和 suspend 方法
- currentClass = [currentClass superclass]
使用 AFSecurityPolicy 保证请求的安全
AFSecurityPolicy 具体参考 AFNetworking3源码阅读-Security
在代理方法 URLSession:didReceiveChallenge:completionHandler
中调用
1 | ... |