源碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking

該文章閱讀的AFNetworking的版本爲3.2.0。bash

這個分類提供了爲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的關聯對象爲分類添加屬性保存進度條的動畫選項async

- (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的回調中更新其進度。post

源碼閱讀系列:AFNetworking動畫

源碼閱讀:AFNetworking(一)——從使用入手ui

源碼閱讀:AFNetworking(二)——AFURLRequestSerializationspa

源碼閱讀:AFNetworking(三)——AFURLResponseSerialization3d

源碼閱讀:AFNetworking(四)——AFSecurityPolicycode

源碼閱讀:AFNetworking(五)——AFNetworkReachabilityManagerserver

源碼閱讀: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

相關文章
相關標籤/搜索