源码阅读:AFNetworking(十四)——UIProgressView+AFNetworking

793 阅读3分钟

该文章阅读的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(一)——从使用入手

源码阅读: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

源码阅读:AFNetworking(十五)——UIRefreshControl+AFNetworking

源码阅读:AFNetworking(十六)——UIWebView+AFNetworking