AFURLSessionManager
根据作者:
AFURLSessionManager
是基于一个指定的NSURLSessionConfiguration
对象创建的用于管理NSURLSession
对象。 其遵循了NSURLSessionTaskDelegate、NSURLSessionDataDelegate、NSURLSessionDownloadDelegate、NSURLSessionDelegate
协议
## NSURLSession & NSURLSessionTask Delegate Methods
`AFURLSessionManager` implements the following delegate methods:
### `NSURLSessionDelegate`
- `URLSession:didBecomeInvalidWithError:`
- `URLSession:didReceiveChallenge:completionHandler:`
- `URLSessionDidFinishEventsForBackgroundURLSession:`
### `NSURLSessionTaskDelegate`
- `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`
- `URLSession:task:didReceiveChallenge:completionHandler:`
- `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`
- `URLSession:task:needNewBodyStream:`
- `URLSession:task:didCompleteWithError:`
### `NSURLSessionDataDelegate`
- `URLSession:dataTask:didReceiveResponse:completionHandler:`
- `URLSession:dataTask:didBecomeDownloadTask:`
- `URLSession:dataTask:didReceiveData:`
- `URLSession:dataTask:willCacheResponse:completionHandler:`
### `NSURLSessionDownloadDelegate`
- `URLSession:downloadTask:didFinishDownloadingToURL:`
- `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`
- `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`
If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first.
基本功能
- 创建和管理
NSURLSession
- 管理
NSURLSessionTask
- 实现
NSURLSessionDelegate
等协议中的代理方法 - 使用
AFURLSessionManagerTaskDelegate
管理进度 - 使用
_AFURLSessionTaskSwizzling
调剂方法 - 引入
AFSecurityPolicy
保证请求的安全 - 引入
AFNetworkReachabilityManager
监控网络状态
创建和管理NSURLSession
初始化方法
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
//配置会话设置
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
//设置NSURLSessionDelegate代理 和 代理queue
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
//初始化保存dataTask的字典
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//为已有task设置代理
[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];
}
}];
}
NSURLSessionTask
AFURLSessionManager
中提供了一系列的获取dataTask的方法
//upload
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
//download
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
//
- (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,在并行队列中创建task时,因为可能返回的taskIdentifier,先前的complectionHandel被替换为新的,即对第二个任务调用第一个响应.(在iOS8之后问题已修复)
调用了addDelegateForDataTask: uploadProgress: downloadProgress: completionHandler:
方法
- (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] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}
创建了一个AFURLSessionManagerTaskDelegate
对象
并在其内部执行了setDelegate
来设置代理
AFURLSessionManager
就是通过字典mutableTaskDelegatesKeyedByTaskIdentifier
来存储和管理每一个NSURLSessionTask
,它以taskIdentifier
为键存储task
NSURLSessionDelegate
前面说过AFURLSessionManager
遵循了一下代理
- NSURLSessionDelegate
- NSURLSessionTaskDelegate
- NSURLSessionDataDelegate
- NSURLSessionDownloadDelegate
我们在使用AFURLSessionManager
的初始化方法时 设置 session的delegate为self
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
}
遵循这些协议实现其方法,然后将其提供更简洁的block接口
将block存在对应属性中,当代理方法调用时,执行对应的block
- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
self.sessionDidBecomeInvalid = block;
}
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
self.sessionDidReceiveAuthenticationChallenge = block;
}
AFURLSessionManagerTaskDelegate管理进度
主要为task提供进度管理功能,并在task结束时回调,即调用completionHandel
- (instancetype)initWithTask:(NSURLSessionTask *)task {
_mutableData = [NSMutableData data];
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
{
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES;
progress.pausingHandler = ^{
[weakTask suspend];
};
#if AF_CAN_USE_AT_AVAILABLE
if (@available(iOS 9, macOS 10.11, *))
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
{
progress.resumingHandler = ^{
[weakTask resume];
};
}
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
主要设置了uploadProgress
和downloadProgress
的回调,当NSProgress的状态改变时,调用相应的task方法,如:resume和suspend来改变task状态
代理方法 URLSession:task:didCompleteWithError:
当sessionManager收到代理,将消息转发给task对应的AFURLSessionManagerTaskDelegate
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
AFURLSessionManagerTaskDelegate
主要调用传入的completionHandle
,然后发出AFNetworkingTaskDidCompleteNotification
通知
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
__strong AFURLSessionManager *manager = self.manager;
//从mutableData取出数据 设置UserInfo
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
self.mutableData = nil;
}
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
//使用 complectionGroup或者complectionQueue 否则创建一个group和主线程调用completionHandler,并在主线程发出通知
if (error) {
dispatch_group_async(manager.completionGroup, manager.completionQueue, ^{
self.completionHandler(task.response, responseObject, error);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
})
} else {
dispatch_async(url_session_manager_processing_queue(), ^{
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
dispatch_group_async(manager.completionGroup, manager.completionQueue, ^{
self.completionHandler(task.response, responseObject, error);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
})
}
}
}
代理方法 URLSession:dataTask:didReceiveData: 和 - URLSession:downloadTask:didFinishDownloadingToURL:
//在收到数据时调用
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
[self.mutableData appendData:data];
}
//在下载对应文件时调用
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
NSError *fileManagerError = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&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];
}
}
这是为了在调用方法时 发出通知
在load时执行方法替换操作,但是因为NSURLSessionTask在iOS7和8的实现有所不同 导致以下代码有点复杂
在iOS7上,localDataTask
是一个__NSCFLocalDataTask
,它继承自__NSCFLocalSessionTask
,它继承自__NSCFURLSessionTask
在iOS8上,localDataTask
是一个__NSCFLocalDataTask
,它继承自__NSCFLocalSessionTask
,它继承自NSURLSessionTask
在iOS7上,__ NSCFLocalSessionTask
和__NSCFURLSessionTask
是唯一具有自己的resume
和suspend
实现的两个类,而__NSCFLocalSessionTask则不会调用超级。这意味着两个类都需要调整。
NSURLSessionTask
在iOS 8上,是唯一实现
resume和
suspend`的类。这意味着这是唯一需要调整的类。
因为NSURLSession
是使用类群实现的,因此从API请求的类并不是将获得的类的类型.所以直接简单的使用[NSURLSessionTask class]
将不起作用的。需要用NSURLSession
来实际创建一个对象,并从该对象处获取该类
因此实现方案如下
- 通过获取
NSURLSession
实例来获取_NSCFLocalDataTask
实例 - 获取指向
af_resume
的原始实现指针 - 检查当前类是否有
resume
实现 如果有继续执行步骤4 - 获取当前类的父类
- 获取父类的
resume
实现指针 - 获取当前类的
resume
实现指针 - 加入当前类的resume实现与父类不同,并且当前类的resume实现和
af_resume
实现不同,则替换两个方法实现 - 设置当前类为父类 重复步骤3-8
+ (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];
}
}
引入AFSecurityPolicy来保证请求的安全
调用了-[AFSecurityPolicy evaluateServerTrust:forDomain:]
方法来判断当前服务器是否被信任
引入AFNetworkReachabilityManager监控网络状态
AFURLSessionManager
的网络状态由AFNetworkReachabilityManager
监控,并持有一个该对象