该文章阅读的AFNetworking的版本为3.2.0。
这个分类提供了为UIProgressView控件绑定task的方法,从而获取task的上传下载进度
1.接口文件
/**
为指定的上传任务绑定进度控件
*/
- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
animated:(BOOL)animated;
/**
为指定的下载任务绑定进度控件
*/
- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
animated:(BOOL)animated;
2.静态常量
/**
任务发送字节数量上下文标识
*/
static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext;
/**
任务接受字节数量上下文标识
*/
static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext;
3.方法实现
- 属性的访问方法
下面的这四个方法就是通过Runtime的关联对象为分类添加属性保存进度条的动画选项
- (BOOL)af_uploadProgressAnimated {
return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue];
}
- (void)af_setUploadProgressAnimated:(BOOL)animated {
objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)af_downloadProgressAnimated {
return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue];
}
- (void)af_setDownloadProgressAnimated:(BOOL)animated {
objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- 实现接口方法
- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task
animated:(BOOL)animated
{
// 如果任务已经执行完了就不继续向下执行了
if (task.state == NSURLSessionTaskStateCompleted) {
return;
}
// 通过KVO观察task的state和countOfBytesSent属性
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
[task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext];
// 保存动画选项
[self af_setUploadProgressAnimated:animated];
}
- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task
animated:(BOOL)animated
{
// 如果任务已经执行完了就不继续向下执行了
if (task.state == NSURLSessionTaskStateCompleted) {
return;
}
// 通过KVO观察task的state和countOfBytesReceived属性
[task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
// 保存动画选项
[self af_setDownloadProgressAnimated:animated];
}
- KVO回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(__unused NSDictionary *)change
context:(void *)context
{
// 如果是通过这个分类添加的观察者
if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) {
// 如果是task的countOfBytesSent属性发生了变化
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
// 如果有要发送的总大小
if ([object countOfBytesExpectedToSend] > 0) {
// 主队列异步调用
dispatch_async(dispatch_get_main_queue(), ^{
// 计算当前的发送进度并为控件赋值
[self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated];
});
}
}
// 如果是task的countOfBytesReceived属性发生了变化
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
// 如果有要接受的总大小
if ([object countOfBytesExpectedToReceive] > 0) {
// 主队列异步调用
dispatch_async(dispatch_get_main_queue(), ^{
// 计算当前的接受进度并为控件赋值
[self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated];
});
}
}
// 如果是task的state属性发生了变化
if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
// 如果状态变成了已完成,就移除掉对task属性的观察
if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
@try {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];
if (context == AFTaskCountOfBytesSentContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
}
if (context == AFTaskCountOfBytesReceivedContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
}
}
@catch (NSException * __unused exception) {}
}
}
}
}
4.总结
可以看到,绑定进度条控件的功能主要是通过KVO观察task的相关属性实现的,在KVO的回调中更新其进度。
源码阅读系列:AFNetworking
源码阅读:AFNetworking(二)——AFURLRequestSerialization
源码阅读:AFNetworking(三)——AFURLResponseSerialization
源码阅读:AFNetworking(四)——AFSecurityPolicy
源码阅读:AFNetworking(五)——AFNetworkReachabilityManager
源码阅读:AFNetworking(六)——AFURLSessionManager
源码阅读:AFNetworking(七)——AFHTTPSessionManager
源码阅读:AFNetworking(八)——AFAutoPurgingImageCache
源码阅读:AFNetworking(九)——AFImageDownloader
源码阅读:AFNetworking(十)——AFNetworkActivityIndicatorManager
源码阅读:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking
源码阅读:AFNetworking(十二)——UIButton+AFNetworking
源码阅读:AFNetworking(十三)——UIImageView+AFNetworking
源码阅读:AFNetworking(十四)——UIProgressView+AFNetworking