AFURLSessionManager
绝对可以称得上是 AFNetworking 的核心。
NSURLSession
NSURLSessionTask
NSURLSessionDelegate
等协议中的代理方法 AFURLSessionManagerTaskDelegate
管理进度 _AFURLSessionTaskSwizzling
调剂方法 AFSecurityPolicy
保证请求的安全 AFNetworkReachabilityManager
监控网络状态 我们会在这里着重介绍上面七个功能中的前五个,分析它是如何包装 NSURLSession
以及众多代理方法的。
NSURLSession
在使用 AFURLSessionManager
时,第一件要做的事情一定是初始化:
- (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]; self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; self.lock = [[NSLock alloc] init]; self.lock.name = AFURLSessionManagerLockName; #1: 为已有的 task 设置代理, 略 return self; }
在初始化方法中,需要完成初始化一些自己持有的实例:
defaultSessionConfiguration
NSURLSessionTask
接下来,在获得了 AFURLSessionManager
的实例之后,我们可以通过以下方法创建 NSURLSessionDataTask
的实例:
- (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; - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler; ... - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler; ...
这里省略了一些返回 NSURLSessionTask
的方法,因为这些接口的形式都是差不多的。
我们将以 - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:]
方法的实现为例,分析它是如何实例化并返回一个 NSURLSessionTask
的实例的:
- (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; }
url_session_manager_create_task_safely
的调用是因为苹果框架中的一个 bug #2093 ,如果有兴趣可以看一下,在这里就不说明了
- [NSURLSession dataTaskWithRequest:]
方法传入 NSURLRequest
- [AFURLSessionManager addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler:]
方法返回一个 AFURLSessionManagerTaskDelegate
对象 completionHandler
uploadProgressBlock
和 downloadProgressBlock
传入该对象并在相应事件发生时进行回调 - (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:]
来设置代理:
- (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]; }
正如上面所提到的, AFNRUSessionManager
就是通过字典 mutableTaskDelegatesKeyedByTaskIdentifier
来存储并管理每一个 NSURLSessionTask
,它以 taskIdentifier
为键存储 task。
该方法使用 NSLock
来保证不同线程使用 mutableTaskDelegatesKeyedByTaskIdentifier
时,不会出现 线程竞争 的问题。
同时调用- setupProgressForTask:,我们会在下面具体介绍这个方法。
NSURLSessionDelegate
等协议中的代理方法 在 AFURLSessionManager
的头文件中可以看到,它遵循了多个协议,其中包括:
NSURLSessionDelegate
NSURLSessionTaskDelegate
NSURLSessionDataDelegate
NSURLSessionDownloadDelegate
它在初始化方法 - [AFURLSessionManager initWithSessionConfiguration:]
将 NSURLSession
的代理指向 self
,然后 实现这些方法 ,提供更简洁的 block 的接口:
- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block; - (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block; ...
它为所有的代理协议都提供了对应的 block 接口,方法实现的思路都是相似的,我们以 - [AFNRLSessionManager setSessionDidBecomeInvalidBlock:]
为例。
首先调用 setter 方法,将 block 存入 sessionDidBecomeInvalid
属性中:
- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block { self.sessionDidBecomeInvalid = block; }
当代理方法调用时,如果存在对应的 block,会执行对应的 block:
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { if (self.sessionDidBecomeInvalid) { self.sessionDidBecomeInvalid(session, error); } [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; }
其他相似的接口实现也都差不多,这里直接跳过了。
AFURLSessionManagerTaskDelegate
管理进度 在上面我们提到过 AFURLSessionManagerTaskDelegate
类,它主要为 task 提供 进度管理 功能,并在 task 结束时 回调 , 也就是调用在 - [AFURLSessionManager dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:]
等方法中传入的 completionHandler
。
我们首先分析一下 AFURLSessionManagerTaskDelegate
是如何对进度进行跟踪的:
- (void)setupProgressForTask:(NSURLSessionTask *)task { #1:设置在上传进度或者下载进度状态改变时的回调 #2:KVO }
该方法的实现有两个部分,一部分是对代理持有的两个属性 uploadProgress
和 downloadProgress
设置回调
__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
属性进行键值观测:
[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
- (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
通知 - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { #1:获取数据, 存储 `responseSerializer` 和 `downloadFileURL` if (error) { #2:在存在错误时调用 `completionHandler` } else { #3:调用 `completionHandler` } }
这是整个代理方法的骨架,先看一下最简单的第一部分代码:
__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; }
这部分代码从 mutableData
中取出了数据,设置了 userInfo
。
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 并发送通知。
dispatch_async(url_session_manager_processing_queue(), ^{ NSError *serializationError = nil; 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]; }); }); });
代理方法 URLSession:dataTask:didReceiveData:
和 - URLSession:downloadTask:didFinishDownloadingToURL:
这两个代理方法分别会在收到数据或者完成下载对应文件时调用,作用分别是为 mutableData
追加数据和处理下载的文件:
- (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
方法,使用下面的方法替换原有的实现
- (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
方法只会在整个文件被引入时调用一次
+ (void)load { if (NSClassFromString(@"NSURLSessionTask")) { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration]; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnonnull" NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil]; #pragma clang diagnostic pop IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); Class currentClass = [localDataTask class]; while (class_getInstanceMethod(currentClass, @selector(resume))) { Class superClass = [currentClass superclass]; IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); if (classResumeIMP != superclassResumeIMP && originalAFResumeIMP != classResumeIMP) { [self swizzleResumeAndSuspendMethodForClass:currentClass]; } currentClass = [currentClass superclass]; } [localDataTask cancel]; [session finishTasksAndInvalidate]; } }
NSClassFromString(@"NSURLSessionTask")
判断当前部署的 iOS 版本是否含有类 NSURLSessionTask
NSURLSessionTask
的实现不同,所以会通过 - [NSURLSession dataTaskWithURL:]
方法返回一个 NSURLSessionTask
实例 _AFURLSessionTaskSwizzling
中的实现 af_resume
currentClass
有 resume
方法 swizzleResumeAndSuspendMethodForClass:
调剂该类的 resume
和 suspend
方法 currentClass = [currentClass superclass]
这里复杂的实现是为了解决 bug #2702
AFSecurityPolicy
保证请求的安全 AFSecurityPolicy
是 AFNetworking
用来保证 HTTP 请求安全的类,它被 AFURLSessionManager
持有,如果你在 AFURLSessionManager
的实现文件中搜索 self.securityPolicy ,你只会得到三条结果:
self.securityPolicy = [AFSecurityPolicy defaultPolicy]
在 API 调用上,后两者都调用了 - [AFSecurityPolicy evaluateServerTrust:forDomain:]
方法来判断 当前服务器是否被信任 ,我们会在接下来的文章中具体介绍这个方法的实现的作用。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __block NSURLCredential *credential = nil; if (self.taskDidReceiveAuthenticationChallenge) { disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); } else { if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { disposition = NSURLSessionAuthChallengeUseCredential; credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; } else { disposition = NSURLSessionAuthChallengeRejectProtectionSpace; } } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; } } if (completionHandler) { completionHandler(disposition, credential); } }
如果没有传入 taskDidReceiveAuthenticationChallenge
block,只有在上述方法返回 YES
时,才会获得认证凭证 credential
。
AFNetworkReachabilityManager
监控网络状态 与 AFSecurityPolicy
相同, AFURLSessionManager
对网络状态的监控是由 AFNetworkReachabilityManager
来负责的,它仅仅是持有一个 AFNetworkReachabilityManager
的对象。
真正需要判断网络状态时,仍然 需要开发者调用对应的 API 获取网络状态 。
AFURLSessionManager
是对 NSURLSession
的封装 - [AFURLSessionManager dataTaskWithRequest:completionHandler:]
等接口创建 NSURLSessionDataTask
的实例 mutableTaskDelegatesKeyedByTaskIdentifier
管理这些 data task 实例 AFURLSessionManagerTaskDelegate
来对传入的 uploadProgressBlock
downloadProgressBlock
completionHandler
在合适的时间进行调用 关于其他 AFNetworking 源代码分析的其他文章: