原文链接:http://itangqi.me/2016/05/09/the-notes-of-learning-afnetworking-three/
前言
AFURLSessionManager
绝对可以称得上是 AFNetworking 的核心,所以本文篇幅会相对长一点,但我保证绝对是满满的干货~
AFURLSessionManager
首先,在 AFURLSessionManager.h
中关于 AFURLSessionManager
的概述:
AFURLSessionManager
creates and manages anNSURLSession
object based on a specifiedNSURLSessionConfiguration
object, which conforms to<NSURLSessionTaskDelegate>
,<NSURLSessionDataDelegate>
,<NSURLSessionDownloadDelegate>
, and<NSURLSessionDelegate>
.
最终可以归结为以下几点:
- 负责创建和管理
NSURLSession
- 管理
NSURLSessionTask
- 实现
NSURLSessionDelegate
等协议中的代理方法 - 使用
AFURLSessionManagerTaskDelegate
管理进度 - 使用
_AFURLSessionTaskSwizzling
调剂方法 - 引入
AFSecurityPolicy
保证请求的安全 - 引入
AFNetworkReachabilityManager
监控网络状态
创建和管理 NSURLSession
按惯例,我们由 AFURLSessionManager
的初始化方法:- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
进行展开:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { self = [super init]; if (!self) { return nil; } if (!configuration) { configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; } self.sessionConfiguration = configuration; self.operationQueue = [[NSOperationQueue alloc] init]; self.operationQueue.maxConcurrentOperationCount = 1; self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.securityPolicy = [AFSecurityPolicy defaultPolicy]; #if !TARGET_OS_WATCH self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; #endif self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; self.lock = [[NSLock alloc] init]; self.lock.name = AFURLSessionManagerLockName; [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; } |
该方法主要完成如下工作:
- 初始化会话配置(NSURLSessionConfiguration),默认为
defaultSessionConfiguration
- 设置相应的
OperationQueue
,决定请求过程中的一系列事件在哪个OperationQueue
回调,这里是设置了最大并发量为 1 的队列,也就相当于串行队列了。(AFNetworing 2.0 版本是设置了一条常驻线程来响应所有网络请求的 delegate 事件) - 初始化会话(session),并设置会话的代理及代理队列,delegate 用来处理请求中的各种事件,可以设置为 nil 使用系统提供的 delegate,但是要想支持后台传输数据必须提供自定义实现的 delegate;另外,
NSURLSession
对象是强引用了 delegate,如果程序最终没有调用invalidateAndCancel
方法来 invalidate 该 session 的话,则会造成内存泄漏 - 初始化管理响应序列化(AFJSONResponseSerializer),安全认证(AFSecurityPolicy)以及监控网络状态(AFNetworkReachabilityManager)的实例
- 初始化保存 data task 的字典(mutableTaskDelegatesKeyedByTaskIdentifier)
管理 NSURLSessionTask
接下来,在获得了 AFURLSessionManager
的实例之后,我们可以通过以下方法创建 NSURLSessionDataTask
的实例:
1 2 3 4 5 6 7 8 9 10 11 12 | /** Creates an `NSURLSessionDataTask` with the specified request. @param request The HTTP request for the request. @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. */ - (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; ... |
这里省略了一些返回 NSURLSessionTask
的方法,因为这些接口的形式都是差不多的。
扩展:Difference between nullable, __nullable and _Nullable in Objective-C
下面,我们将以 - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:]
方法的实现为例,分析它是如何实例化并返回一个 NSURLSessionTask
的实例的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | - (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 { __block NSURLSessionDataTask *dataTask = nil; url_session_manager_create_task_safely(^{ dataTask = [self.session dataTaskWithRequest:request]; }); [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; return dataTask; } |
该方法主要完成如下工作:
- 调用
- [NSURLSession dataTaskWithRequest:]
方法传入NSURLRequest
- 调用
- [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:]
方法返回一个AFURLSessionManagerTaskDelegate
对象 - 将
completionHandler
uploadProgressBlock
和downloadProgressBlock
传入该对象并在相应事件发生时进行回调
url_session_manager_create_task_safely
的调用是因为苹果框架中的一个 bug #2093,如果有兴趣可以看一下,在这里就不具体说明了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | - (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] init]; delegate.manager = self; delegate.completionHandler = completionHandler; dataTask.taskDescription = self.taskDescriptionForSessionTasks; [self setDelegate:delegate forTask:dataTask]; delegate.uploadProgressBlock = uploadProgressBlock; delegate.downloadProgressBlock = downloadProgressBlock; } |
在这个方法中同时调用了另一个方法 - [AFURLSessionManager setDelegate:forTask:]
来设置代理:
1 2 3 4 5 6 7 8 9 10 11 12 | - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task { #1: 检查参数, 略 [self.lock lock]; self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; [delegate setupProgressForTask:task]; [self addNotificationObserverForTask:task]; [self.lock unlock]; } |
正如上面所提到的,AFURLSessionManager
就是通过字典 mutableTaskDelegatesKeyedByTaskIdentifier
来存储并管理每一个 NSURLSessionTask
,它以 taskIdentifier
为键存储 task。
该方法使用 NSLock
来保证不同线程使用 mutableTaskDelegatesKeyedByTaskIdentifier
时,不会出现线程竞争的问题(既线程同步)。
同时调用 - setupProgressForTask:,我们会在下面具体介绍这个方法。
实现 NSURLSessionDelegate
等协议中的代理方法
首先,NSURLSession
的代理对象结构如下:
接下来,我们来看下具体的代理方法:
NSURLSessionDelegate
NSURLSessionTaskDelegate
,遵守NSURLSessionDelegate
协议-
NSURLSessionDataDelegate
,遵守NSURLSessionTaskDelegate
协议,是网络请求通常遵循的协议,常用的方法:-
接受到服务响应时调用的方法
1 2 3 4 5
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler; /** * 必须在该方法中对服务器的响应进行授权,才能继续接收服务器返回的数据,调用如下函数 * completionHandler(NSURLSessionResponseAllow) */
-
接收到服务器返回的数据时调用的方法
1 2 3 4
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data /** * data:服务返回的数据,通常为 JSON 格式数据 */
-
请求完成时调用的方法(成功或失败)
1 2 3 4
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error /** * 若出现错误,error 中存放错误信息 */
-
-
NSURLSessionDownloadDelegate(通常用于下载大量数据),遵守 NSURLSessionTaskDelegate 协议,常用的方法:
-
写入数据到临时文件时调用的方法(服务器返回一点就写入一点)
1 2 3 4 5 6
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite: (int64_t)totalBytesExpectedToWrite /** * totalBytesWritten,已写入数据的总长度 * totalBytesExpectedToWrite:总共要写入数据的总长度 * 可以在该方法中计算下载进度 */
-
遇到错误的时候调用
1 2 3 4
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error /** *error:若遇到错误,则保存错误信息 */
-
用于断点下载的方法
1 2 3 4 5
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes /** * fileOffset:继续下载时,文件的开始位置 * expectedTotalBytes:剩余的数据总数 */
-
下载完成时调用的方法
1 2 3 4 5
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location /** * location:下载的文件保存的临时位置 * 需要将下载的文件保存在可以长期保存的位置 */
-
使用 AFURLSessionManagerTaskDelegate
管理进度
在上面我们提到过 AFURLSessionManagerTaskDelegate
类,它主要为 task 提供进度管理功能,并在 task 结束时回调, 也就是调用在 - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:]
等方法中传入的 completionHandler
。
我们首先分析一下 AFURLSessionManagerTaskDelegate
是如何对进度进行跟踪的:
1 2 3 4 5 6 7 | - (void)setupProgressForTask:(NSURLSessionTask *)task { #1:设置在上传进度或者下载进度状态改变时的回调 true #2:KVO } |
该方法的实现有两个部分,一部分是对代理持有的两个属性 uploadProgress
和 downloadProgress
设置回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | __weak __typeof__(task) weakTask = task; self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; [self.uploadProgress setCancellable:YES]; [self.uploadProgress setCancellationHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask cancel]; }]; [self.uploadProgress setPausable:YES]; [self.uploadProgress setPausingHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask suspend]; }]; if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) { [self.uploadProgress setResumingHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask resume]; }]; } |
这里只有对 uploadProgress
设置回调的代码,设置 downloadProgress
与这里完全相同
主要目的是在对应
NSProgress
的状态改变时,调用resume
suspend
等方法改变 task 的状态。
第二部分是对 task 和 NSProgress
属性进行键值观测:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived)) options:NSKeyValueObservingOptionNew context:NULL]; [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive)) options:NSKeyValueObservingOptionNew context:NULL]; [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent)) options:NSKeyValueObservingOptionNew context:NULL]; [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend)) options:NSKeyValueObservingOptionNew context:NULL]; [self.downloadProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL]; [self.uploadProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL]; |
在 observeValueForKeypath:ofObject:change:context:
方法中改变进度,并调用 block
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { if ([object isKindOfClass:[NSURLSessionTask class]]) { if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { self.downloadProgress.completedUnitCount = [change[@"new"] longLongValue]; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) { self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue]; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue]; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) { self.uploadProgress.totalUnitCount = [change[@"new"] longLongValue]; } } else if ([object isEqual:self.downloadProgress]) { if (self.downloadProgressBlock) { self.downloadProgressBlock(object); } } else if ([object isEqual:self.uploadProgress]) { if (self.uploadProgressBlock) { self.uploadProgressBlock(object); } } } |
对象的某些属性改变时更新 NSProgress
对象或使用 block 传递 NSProgress
对象 self.uploadProgressBlock(object)
。
代理方法 URLSession:task:didCompleteWithError:
在每一个 NSURLSessionTask
结束时,都会在代理方法 URLSession:task:didCompleteWithError:
中:
- 调用传入的
completionHander
block - 发出
AFNetworkingTaskDidCompleteNotification
通知
1 2 3 4 5 6 7 8 9 10 11 12 | - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { #1:获取数据, 存储 `responseSerializer` 和 `downloadFileURL` if (error) { #2:在存在错误时调用 `completionHandler` } else { truetrue#3:调用 `completionHandler` } } |
这是整个代理方法的骨架,先看一下最简单的第一部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; // 具体可以查看 #issue 2672。这里主要是针对大文件的时候,性能提升会很明显 NSData *data = nil; if (self.mutableData) { data = [self.mutableData copy]; // 此处不再需要 mutableData 了 self.mutableData = nil; } if (self.downloadFileURL) { userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; } else if (data) { userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; } |
再来看第二部分:这部分代码从 mutableData
中取出了数据,设置了 userInfo
。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 如果 task 出错了,处理 error 信息 // 所以对应的观察者在处理 error 的时候,比如可以先判断 userInfo[AFNetworkingTaskDidCompleteErrorKey] 是否有值,有值的话,就说明是要处理 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]; }); }); |
如果当前 manager
持有 completionGroup
或者 completionQueue
就使用它们。否则会创建一个 dispatch_group_t
并在主线程中调用 completionHandler
并发送通知(在主线程中)。
最后一部分:如果在执行当前 task 时没有遇到错误,那么先对数据进行序列化,然后同样调用 block 并发送通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | dispatch_async(url_session_manager_processing_queue(), ^{ NSError *serializationError = nil; // 根据对应的 task 和 data 将 response data 解析成可用的数据格式,比如 JSON serializer 就将 data 解析成 JSON 格式 responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; // 注意如果有 downloadFileURL,意味着 data 存放在了磁盘上了,所以此处 responseObject 保存的是 data 存放位置,供后面 completionHandler 处理。没有 downloadFileURL,就直接使用内存中的解析后的 data 数据 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]; }); }); }); |
代理方法 URLSession:dataTask:didReceiveData:
和 - URLSession:downloadTask:didFinishDownloadingToURL:
这两个代理方法分别会在收到数据或者完成下载对应文件时调用,作用分别是为 mutableData
追加数据和处理下载的文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | - (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [self.mutableData appendData:data]; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSError *fileManagerError = nil; self.downloadFileURL = nil; if (self.downloadTaskDidFinishDownloading) { self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); if (self.downloadFileURL) { [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; if (fileManagerError) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; } } } } |
使用 _AFURLSessionTaskSwizzling
调剂方法
_AFURLSessionTaskSwizzling
的唯一功能就是修改 NSURLSessionTask
的 resume
和 suspend
方法,使用下面的方法替换原有的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | - (void)af_resume { NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_resume]; if (state != NSURLSessionTaskStateRunning) { [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; } } - (void)af_suspend { NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_suspend]; if (state != NSURLSessionTaskStateSuspended) { [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; } } |
这样做的目的是为了在方法 resume
或者 suspend
被调用时发出通知。
具体方法调剂的过程是在 + load
方法中进行的
load
方法只会在整个文件被引入时调用一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | + (void)load { if (NSClassFromString(@"NSURLSessionTask")) { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; // 首先构建一个 NSURLSession 对象 session,再通过 session 构建出一个 _NSCFLocalDataTask 变量 NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration]; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnonnull" NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil]; #pragma clang diagnostic pop // 获取到 af_resume 实现的指针 IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); Class currentClass = [localDataTask class]; // 检查当前 class 是否实现了 resume。如果实现了,继续第 4 步 while (class_getInstanceMethod(currentClass, @selector(resume))) { // 获取到当前 class 的父类(superClass) Class superClass = [currentClass superclass]; // 获取到当前 class 对于 resume 实现的指针 IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); // 获取到父类对于 resume 实现的指针 IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); // 如果当前 class 对于 resume 的实现和父类不一样(类似 iOS 7 上的情况),并且当前 class 的 resume 实现和 af_resume 不一样,才进行 method swizzling if (classResumeIMP != superclassResumeIMP && originalAFResumeIMP != classResumeIMP) { [self swizzleResumeAndSuspendMethodForClass:currentClass]; } // 设置当前操作的 class 为其父类 class,重复步骤 3~8 currentClass = [currentClass superclass]; } [localDataTask cancel]; [session finishTasksAndInvalidate]; } } |
- 首先用
NSClassFromString(@"NSURLSessionTask")
判断当前部署的 iOS 版本是否含有类NSURLSessionTask
- 因为 iOS 7 和 iOS 8 上对于
NSURLSessionTask
的实现不同,所以会通过- [NSURLSession dataTaskWithURL:]
方法返回一个NSURLSessionTask
实例 - 取得当前类
_AFURLSessionTaskSwizzling
中的实现af_resume
- 如果当前类
currentClass
有resume
方法- 真:5
- 假:6
- 使用
swizzleResumeAndSuspendMethodForClass:
调剂该类的resume
和suspend
方法 currentClass = [currentClass superclass]
这里复杂的实现是为了解决 bug #2702
引入 AFSecurityPolicy
保证请求的安全
AFSecurityPolicy
是 AFNetworking
用来保证 HTTP 请求安全的类,它被 AFURLSessionManager
持有,如果你在 AFURLSessionManager
的实现文件中搜索 self.securityPolicy,你只会得到三条结果:
- 初始化
self.securityPolicy = [AFSecurityPolicy defaultPolicy]
- 收到连接层的验证请求时
- 任务接收到验证请求时
在 API 调用上,后两者都调用了 - [AFSecurityPolicy evaluateServerTrust:forDomain:]
方法来判断当前服务器是否被信任,我们会在接下来的文章中具体介绍这个方法的实现的作用。
引入 AFNetworkReachabilityManager
监控网络状态
与 AFSecurityPolicy
相同,AFURLSessionManager
对网络状态的监控是由 AFNetworkReachabilityManager
来负责的,它仅仅是持有一个 AFNetworkReachabilityManager
的对象。
真正需要判断网络状态时,仍然需要开发者调用对应的 API 获取网络状态。
小结
AFURLSessionManager
是对NSURLSession
的封装- 它通过
- [AFURLSessionManager dataTaskWithRequest:completionHandler:]
等接口创建NSURLSessionDataTask
的实例 - 持有一个字典
mutableTaskDelegatesKeyedByTaskIdentifier
管理这些 data task 实例 - 引入
AFURLSessionManagerTaskDelegate
来对传入的uploadProgressBlock
downloadProgressBlock
completionHandler
在合适的时间进行调用 - 实现了全部的代理方法来提供 block 接口
- 通过方法调剂在 data task 状态改变时,发出通知