該文章閱讀的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