前面两篇笔记已经介绍了AFHTTPSessionManager
,本文将会深入到整个网络库的核心类AFURLSessionManager
.
我们指定AFNetworking3.x使用的是NSURLSession
,根据请求的不同通过它创建不同的task(dataTaskXXX
,uploadTaskXXX
,downloadTaskXXX
),不论是session层还是task层,产生的事件都需要异步通过delegate处理.相关的delegate:
- NSURLSessionDelegate
- NSURLSessionTaskDelegate
- NSURLSessionDataDelegate
- NSURLSessionDownloadDelegate
AFURLSessionManager
就充当着管理session
,创建task
,处理上述的各种delegate
事件的作用.
核心类的初始化
在AFURLSessionManager
的指定init方法中:
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
// 1 创建session的配置类
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
// 2 创建session 的delegate运行的queue
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
// 3 创建session实例,并且强引用,delegate 是自己
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
// 4 创建response 接受的数据序列化的辅助类,默认接受JSON数据
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 5 配置安全策略 -> 主要在delegate中收到鉴权请求时候使用
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
// 6 创建网络状态监听类 -> 但是实际中框架里面好像没怎么使用???
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 7 创建dict,用来管理task对应的delegate,因为有部分内容交给 delegate helper 类去完成的
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// 8 [task:delegateHelper] 访问时候的锁
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 9 为什么要调用这个方法??????有点奇怪-> 方法的主要作用是获取session中所有的真该执行的tasks
[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;
}
task delegate helper类
几乎核心类所有的方法都是围绕session的delegate方法展开的.不论是session层的delegate,还是session产生的task层的delegate方法的实现都在AFURLSessionManager
实现.
为了区分不同task产生的delegate事件,AFNetworking引入了AFURLSessionManagerTaskDelegate
类.该类主要帮助每个task完成两大块的任务:
- 1 task执行下载完成任务以后的completionHanlder由delegate helper类执行的
- 2 在以下几个delegate中帮助特定的task处理delegate事件
- 3 完成关于NSProgress相关上传或者下载的进度的progressBlock
// NSURLSessionTaskDelegate, 这个帮助方法也是dataTask完成请求以后,responseSerializer的入口
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;
// NSURLSessionDataDelegate
- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
//NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
再了解框架是如何使用该类来帮助完成这两块任务之前,我们先来看下AFURLSessionManager
是如何使用该类来绑定不同的task的.
在AFURLSessionManager
的dataTaskWithRequest:xxxxx
方法使用session创建Task(dataTask,uploadTask,downloadTask)以后,都会调用该类的另外一簇方法,将刚刚创建的task实例当做参数传递进去.
下面是addDelegateForXXXTask:XXXProgress:...:completionHanlder
方法:
- (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;
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler;
为了说明delegate helper 是如何与task绑定,又如何在代理方法中分发的,这里举一个简单的例子:
该方法簇是创建一个该类的实例,然后将它与刚刚创建的task关联起来,存放到AFURLSessionManager
的mutableTaskDelegatesKeyedByTaskIdentifier
的NSDictionary实例中,当调用上文提到的几个delegate方法时候,就会根据不同的task调用不同task.identifier
为key的AFURLSessionManagerTaskDelegate
对象完成处理.简而言之,AFURLSessionManager
就犹如一个路由,给不同的task任务派发不同的事件.具体事件的处理函数都在各自task对应的task delegate helper方法中去完成,具体的实例代码如下:
/**
* 将dataTask和一个 AFURLSessionManagerTaskDelegate 关联起来,将其中部分progressBlock交给delegate去管理
*/
- (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
{
// 1 创建task delegate helper 类
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
// 2 delegate helper 也会在task收到执行完毕的回调以后执行completionHanlder
delegate.completionHandler = completionHandler;
// 3 用来标识task唯一性!!!!用的task的内存地址,将dataTask <--> delegate helper关联起来
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
// 4 NSProgress相关的Block都是由delegate helper去更新的
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
// 通知delegate, task已经完成, 会直接调用 taskDelegateHelper -> 将数据通过responseSerializer处理
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
// 1 获取delegate -> 2 调用delegate 的相似方法 -> 3 从dict中删除(删除以后,注意清理现场)
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
// 2 使用task对应的delegate helper 执行该代理任务
[delegate URLSession:session task:task didCompleteWithError:error];
// 3 将task从[task:delegate]中删除
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
task delegate helper 的存取
由于session创建的每个task都有一个对应的AFURLSessionManagerTaskDelegate
,因此在访问manager的mutableTaskDelegatesKeyedByTaskIdentifier
字典时候,不可避免的遇到字典的读写同步问题.框架中通过一个NSLock
保证读写时可能面临的同步问题.对数据只有 -- 增, 删, 查
// 增
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
// 访问 mutableTaskDelegatesKeyedByTaskIdentifier 字典时候需要加锁,在其他存储时候也需要加锁
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;//使用task的identifier作为key
[delegate setupProgressForTask:task];// 为task设置NSProgress的重要内容
[self addNotificationObserverForTask:task];// 监听task完成或者恢复时候发送的通知
[self.lock unlock];
}
// 删
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[self.lock lock];
[delegate cleanUpProgressForTask:task];
[self removeNotificationObserverForTask:task];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
// 改 -> 没有对task进行修改 -> 代码里面用的先删除然后设置的方法
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
if (delegate) {
[self removeDelegateForTask:dataTask];
[self setDelegate:delegate forTask:downloadTask];
}
if (self.dataTaskDidBecomeDownloadTask) {
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
// 查
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
// 使用加锁的方式获取task对应的delegate
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];// 通过taskIdentifier完成
[self.lock unlock];
return delegate;
}
其他学习的地方
AFURLSessionManager
中除了delegate方法,还有一组关于正在运行的session task属性的方法簇:
///----------------------------
/// @name Getting Session Tasks
///----------------------------
/**
The data, upload, and download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
/**
The data tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
/**
The upload tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
/**
The download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
这些方法簇的getter实现的也比较巧妙:
- (NSArray *)tasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];// _cmd 表示方法名称
}
- (NSArray *)dataTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)uploadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)downloadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
// 通过keyPath 获取对应的tasks array. -> 使用dispatch_semephore_t 等待异步调用完成,然后执行的方法
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
// 利用信号量异步等待的方法!!!!
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {// 异步调用block
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
其中,在tasksForKeyPath
方法中犹豫,session获取正在执行的tasks是使用的异步block返回查询结果的.因此框架中使用了dispatch_semaphore_t
来等待异步结果,可以在以后工作中学以致用:
// 1 创建信号量 -> 初始计数是0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
block(){//异步block方法中给信号量发信息
...
// 2 在block异步结果返回时候发送信号
dispatch_semaphore_signal(semaphore);
}
//3 等待信号量的数据以后进行后面内容的执行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
...
网友评论