AFNetworking源码阅读3——代理
前言
在上篇AFNetworking源码阅读2——核心快结尾时,解释了为什么在AFURLSessionManager
类中实现了session和 session task的代理方法,还要在AFURLSessionManagerTaskDelegate
代理类中再实现一遍session task的代理方法?AFURLSessionManagerTaskDelegate
代理类存在的意义是什么?
原因我们已经知道,是将我们请求所得的数据,或感兴趣的信息等抽离在一个专门类中。然后再以此类回调给使用者调用的方法,回馈给使用者。
该代理类的作用基本就是这样,但我们还是需要看看具体实现,学习学习。
源码
先看头文件:
屏幕快照 2016-10-14 下午2.02.21.png@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSProgress *uploadProgress;
@property (nonatomic, strong) NSProgress *downloadProgress;
@property (nonatomic, copy) NSURL *downloadFileURL; // 所下载文件的磁盘路径
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
@end
首先可以看到该类是实现了session task的三个协议的。manager
意为对应的AFURLSessionManager
实例;mutableData
意为从网络返回的数据;uploadProgress
和downloadProgress
意为上传进度和下载进度;downloadFileURL
意为所下载文件的磁盘路径;后面几个是定义的block属性,顾名思义,它们分别代表下载完成后的回调block,上传进度发生改变时的回调block,下载进度发生改变时的回调block,完成后的处理回调block。它们的类型定义如下,留心其返回值和参数。
typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
接下来该类具体怎么实现的:
屏幕快照 2016-10-14 下午2.03.21.png可以看到,该类的结构比较简单,篇幅也不长。主要有三部分:初始化,进度跟踪,代理方法的实现。我们按顺序来,先看看初始化方法:
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.mutableData = [NSMutableData data];
self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
self.uploadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;
self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
self.downloadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;
return self;
}
初始化方法没什么可说的,主要是在其中初始化了属性。
接着看对task对应的进度追踪代码:
#pragma mark - NSProgress Tracking
/*
主要是设置两个NSProgress类型变量的uploadProgress和downloadProgress属性
*/
- (void)setupProgressForTask:(NSURLSessionTask *)task {
__weak __typeof__(task) weakTask = task;
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
/*
下面设置了uploadProgress和downloadProgress的三个属性:cancel/pause/resume。正好对应session task的cancel/pause/resume三个状态。也就是说进度progress的数据来源于实际上由session task来驱动
*/
[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];
}];
}
[self.downloadProgress setCancellable:YES];
[self.downloadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
[self.downloadProgress setPausable:YES];
[self.downloadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.downloadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}
/*
观察progress的fractionCompleted属性
*/
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.uploadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
setupProgressForTask:
这个方法代码很长,但是内容却比较简单且好理解。简单说就是将下载任务task的数据总量、上传/下载进度等信息赋值给代理类的uploadProgress
或downloadProgress
属性。除此外,并且还实现了uploadProgress
和downloadProgress
俩属性取消、暂停、重启操作的block回调,可以看到它俩这三个操作实际上是由对应的task实现的。也就是说,代理类的进度信息的数据既是task赋给的,其操作动作也是task驱动的。总之,把一切使用者感兴趣的东西都从task抽离出去,置给了代理类。
还没完,在这个方法结尾,给进度的属性fractionCompleted
“完成度百分比”添加了监听,即KVO。只要下载百分比变化,就执行下面监听的代理方法,执行self.downloadProgressBlock(object);
,调用其block回调属性。这里object
便是所监听的属性的主人,即downloadProgress
或uploadProgress
。如此该代理类的uploadProgressBlock
和downloadProgressBlock
俩block回调属性便拥有进度数据了。想一下,该代理类不是有uploadProgress
和downloadProgress
属性吗?为什么还要多此一举,去观察该进度的完成百分比,然后再将进度赋值给进度对应的blockdownloadProgressBlock
和uploadProgressBlock
呢?这是因为我们最终给使用者调用的接口方法是以该类型block回调的,在这里完成block的赋值,然后将回调回去回传给使用层时,直接赋值给同类型的block就行了。
最后来看session task、session dataTask和session downTask代理方法的实现。先看session task代理方法的实现:
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__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;
}
// 若有downloadFileURL,则说明文件下载在磁盘了,downloadFileURL为其路径;反之,数据是存在data里的
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (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];
});
});
} else {
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];
});
});
});
}
}
这个方法里代码很长,但仔细看看,其实也不复杂。其实就是数据获取完成了,想发送一个通知AFNetworkingTaskDidCompleteNotification
,发送该通知时要携带一个NSDictionary
型的参数信息userInfo
,而前面的一大串代码都是为了给该参数赋值。而发出的这个通知,主要用于通知框架里的UI层。
上面的代码使我比较困惑的是?:
符号的含义,x?:y == x?x:y
。一开始我以为这就是个普通的三元运算符,表示无论x真假,都执行y,这样理解是错误的,应当是若x成立,则执行x,否则执行y。
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];
});
});
这段代码里?:
符号所表示的逻辑是:若存在自定义的任务完成时的block回调所在的completionGroup
,则用自定义的;否则用一个方法生成一个专门的,用于完成任务时的block回调所在的group。同理,后半句表示若存在自定义的任务完成时的block回调所在的completionQueue
,则用自定义的;否则用main queue。
这里的逻辑实际上在AFURLSessionManager
头文件里定义这俩属性时便在注释里已有说明:
/**
The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
/**
The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
*/
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
至此,我们把AFURLSessionManagerTaskDelegate
代理类算是看完了,也清楚了它的作用就是把请求网络所返回的数据信息或者错误信息抽离至这个代理类中。现在数据我们看见是抽离在该类里了,但是它是怎么回传给使用者的,我们最好再次梳理一下。
使用者是调用HTTP的便利方法来请求网络获取响应数据的,而响应的数据信息或错误信息是通过downloadProgress
、success
、failure
三个block回调的。
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
// 该方法的目的仍是生成dataTask实例,不过参数更丰富灵活。
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
...
...
这三个block回调的数据信息从哪里得来呢?我们跳进方法里可以看到,它的block数据便从里面这个方法的block而来。
我们再跳进里面这个方法观察。可以看到它的block回调数据同样来自一个内部方法的block回调:
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
...
...
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
...
...
我们继续,再跳进这个方法观察。它的block数据依旧来源于内部一个方法的block回调。
- (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 {
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
...
...
我们再继续,跳进该方法,一切清晰了:该方法的block并没有来自其他的方法回调了,而是来自本篇所讲的代理类的。也就是说,是在这个方法里,请求任务对应的响应数据或错误信息等开始从代理类流出,一层一层,从内至外的回传给使用者调用的HTTP便利接口方法,回传给使用者。
- (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代理类的对象,并为几个属性赋值。然后调用setDelegate:forTask:将其和dataTask绑定
*/
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;
}
结尾
本篇继续解释了代理类存在的作用,并解释了一些具体实现,最后再次梳理了下响应数据的流向。现在算是把AFURLSessionManagerTaskDelegate
类说完了。下篇学习学习序列化。