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;
}
在初始化方法中,需要完成初始化自己持有的实例:
-
初始化会话配置(
NSURLSessionConfiguration
),默认为defaultSessionConfiguration
-
初始化会话(
session
),并设置会话的代理以及代理队列 -
初始化管理响应序列化(
AFJSONResponseSerializer
),安全认证(AFSecurityPolicy
)以及监控网络状态(AFNetworkReachabilityManager
)的实例。 -
初始化保存
data task
的字典(mutableTaskDelegateKeyedByTaskIdentifier
)
二. 管理 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;
...
这里省略了一些返回NSURLSesstionTask
的方法,因为这些接口形式都是差不多,所以我们将以- [AFURLSessionManager dataTaskWithRequest: uploadProgress: downloadProgress:completionHandler:]
方法实例,分析它是如何实例化并返回一个NSURLSestionTask
的实例的。
- (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
static void url_session_manager_create_task_safely(dispatch_block_t block) {
if (NSFoundationVersionNumber <</span> NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
// Fix of bug
// Open Radar:[http://openradar.appspot.com/radar?id=5871104061079552](http://openradar.appspot.com/radar?id=5871104061079552) (status: Fixed in iOS8)
// Issue about:[https://github.com/AFNetworking/AFNetworking/issues/2093](https://github.com/AFNetworking/AFNetworking/issues/2093)
//理解下,第一为什么用sync,因为是想要主线程等在这,等执行完,在返回,因为必须执行完dataTask才有数据,传值才有意义。
//第二,为什么要用串行队列,因为这块是为了防止ios8以下内部的dataTaskWithRequest是并发创建的,
//这样会导致taskIdentifiers这个属性值不唯一,因为后续要用taskIdentifiers来作为Key对应delegate。
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
-
调用
- [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];
}
正如上面所提到的,AFURLSessionManager
就是通过字典 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]; }]; }
这里只有对uploadProgerss
设置回调的代码,设置downloadPregress
回调原理相同主要目的是在对应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.uploadPregressBlock(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调剂方法
_AFURLSessionTaskSwizzing
的唯一功能就是修改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
-
因为
iOS7
和iOS 8
上对于NSURLSessionTask
的实现不同,所以会通过- [NSURLSession dataTaskWithURL:]
方法返回一个NSURLSessionTask
实例。 -
取得当前类
_AFURLSessionTaskSwizzing
中的实现af_resume
-
如果当前类
currentClass
有resume
方法,执行第五步,否则执行第六步。 -
使用
swizzleResumeAndSuspendMethodForClass:
调剂该类的resume
和suspend
方法。 -
currentClass = [currentClass superclass]
六. 引入 AFSecurityPolicy 保证请求的安全
AFSecurityPolicy
是 AFNetworking
用来保证HTTP
请求安全的类,它被AFURLSessionManager
持有,如果AFURLSessionManager
的实现文件中搜索self.securityPlicy
,你只会得到三条搜索结果:
- 初始化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
的实例 -
持有一个字典
mutableTaskDelegatesKeyByTaskIdentifier
管理这些data task
实例。 -
引入
AFURLSessionManagerTaskDelegate
来对传入的uploadProgressBlock downloadProgressBlock、completionHandler
在合适的时间进行调用 -
实现了全部的代理方法来提供
block
接口 -
通过方法调剂在
data task
状态改变时,发出通知。
网友评论