版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.02.28 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請(qǐng)求崭别,都離不開一個(gè)非常有用的框架AFNetworking政溃,可以說這個(gè)框架的知名度已經(jīng)超過了蘋果的底層網(wǎng)絡(luò)請(qǐng)求部分,很多人可能不知道蘋果底層是如何發(fā)起網(wǎng)絡(luò)請(qǐng)求的,但是一定知道
AFNetworking
,接下來幾篇我們就一起詳細(xì)的解析一下這個(gè)框架夯巷。感興趣的可以看上面寫的幾篇。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請(qǐng)求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
回顧
上一篇從GET請(qǐng)求入口開始哀墓,進(jìn)行深入分析趁餐,包括實(shí)例化NSURLSessionDataTask的過程以及為任務(wù)添加代理和通知觀察。本篇會(huì)看一下代理和進(jìn)度之間的關(guān)系以及通知的作用篮绰。
AFURLSessionManagerTaskDelegate代理為任務(wù)設(shè)置進(jìn)度
主要對(duì)應(yīng)的就是下面這一段代碼
- (void)setupProgressForTask:(NSURLSessionTask *)task {
__weak __typeof__(task) weakTask = task;
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
[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];
}];
}
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.uploadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
下面我們就一起看一下這個(gè)是怎么實(shí)現(xiàn)的后雷。
1. 上傳進(jìn)度
關(guān)于上傳進(jìn)度,這里涉及到取消吠各、暫停以及重新開始的回調(diào)和處理臀突。
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
[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];
}];
}
首先就是獲取上傳和下載的總長度,用的就是NSURLSession的屬性走孽。
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
/* number of body bytes we expect to send, derived from the Content-Length of the HTTP request */
@property (readonly) int64_t countOfBytesExpectedToSend;
/* number of byte bytes we expect to receive, usually derived from the Content-Length header of an HTTP response. */
@property (readonly) int64_t countOfBytesExpectedToReceive;
這個(gè)總的字節(jié)數(shù)惧辈,都可以從HTTP頭中獲取。這里還算很清晰了磕瓷,下面簡單的介紹和說明盒齿。
(a) 取消
主要就是下面幾句代碼
[self.uploadProgress setCancellable:YES];
[self.uploadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
首先要設(shè)置的就是可取消cancallable
這個(gè)屬性,需要設(shè)置為YES困食。
/* Whether the work being done can be cancelled or paused, respectively.
By default NSProgresses are cancellable but not pausable. NSProgress is by default
KVO-compliant for these properties, with the notifications always being sent on the thread which updates the property.
These properties are for communicating whether controls for cancelling and pausing should appear in a progress reporting user interface.
NSProgress itself does not do anything with these properties other than help pass their values from progress reporters to progress observers.
It is valid for the values of these properties to change in virtually any way during the lifetime of an NSProgress.
Of course, if an NSProgress is cancellable you should actually implement cancellability by setting a cancellation handler or by making your code poll the result of invoking -isCancelled.
Likewise for pausability.
*/
@property (getter=isCancellable) BOOL cancellable;
所做的工作是否可以分別取消或暫停边翁。 默認(rèn)情況下,NSProgresses是可取消的硕盹,但不可pausable符匾。 對(duì)于這些屬性,NSProgress默認(rèn)為符合KVO標(biāo)準(zhǔn)瘩例,并且通知始終在更新屬性的線程上發(fā)送啊胶。 這些屬性用于傳遞是否應(yīng)該在進(jìn)度報(bào)告用戶界面中顯示取消和暫停的控件。 NSProgress本身不會(huì)對(duì)這些屬性做任何事情垛贤,除了幫助將進(jìn)度記錄的值傳遞給進(jìn)度觀察員焰坪。 在NSProgress的生命周期中,這些屬性的值實(shí)際上以任何方式改變都是有效的聘惦。 當(dāng)然某饰,如果一個(gè)NSProgress可以被取消,你應(yīng)該通過設(shè)置一個(gè)取消處理程序或者讓你的代碼輪詢調(diào)用-isCancelled的結(jié)果來實(shí)現(xiàn)可取消性。 同樣適用于pausability
黔漂。
然后就是在cancelHander中進(jìn)行取消業(yè)務(wù)的處理。
[self.uploadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
這里就是直接調(diào)用任務(wù)Task的取消操作[strongTask cancel]
劳较。
/* -cancel returns immediately, but marks a task as being canceled.
* The task will signal -URLSession:task:didCompleteWithError: with an
* error value of { NSURLErrorDomain, NSURLErrorCancelled }. In some
* cases, the task may signal other work before it acknowledges the
* cancelation. -cancel may be sent to a task that has been suspended.
*/
- (void)cancel;
- cancel
立即返回墓捻,但將任務(wù)標(biāo)記為被取消。 該任務(wù)將發(fā)信號(hào)-URLSession:task:didCompleteWithError:
錯(cuò)誤值為{NSURLErrorDomain,NSURLErrorCancelled}
。 在某些情況下颜骤,任務(wù)可能在確認(rèn)取消之前發(fā)出其他工作的信號(hào)。- cancel
可能被發(fā)送到已被暫停的任務(wù)锈锤。
(b) 暫停
主要就是對(duì)應(yīng)下面幾句代碼
[self.uploadProgress setPausable:YES];
[self.uploadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
我們看一下[strongTask suspend]
/*
* Suspending a task will prevent the NSURLSession from continuing to
* load data. There may still be delegate calls made on behalf of
* this task (for instance, to report data received while suspending)
* but no further transmissions will be made on behalf of the task
* until -resume is sent. The timeout timer associated with the task
* will be disabled while a task is suspended. -suspend and -resume are
* nestable.
*/
- (void)suspend;
- (void)resume;
暫停任務(wù)將阻止NSURLSession繼續(xù)加載數(shù)據(jù)记舆。 可能仍然存在代表此任務(wù)的代理在調(diào)用(例如衣赶,報(bào)告掛起時(shí)收到的數(shù)據(jù))遵馆,但不會(huì)有代表任務(wù)進(jìn)行進(jìn)一步的傳輸直到發(fā)送- resume
换况。 與任務(wù)關(guān)聯(lián)的超時(shí)定時(shí)器將在任務(wù)暫停時(shí)被禁用复隆。 - ususpend
和- resume
是可嵌套的拨匆。
(c) 開始
主要就是對(duì)應(yīng)下面這段代碼
if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.uploadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}
這里說一下這個(gè)resumingHandler
,開始進(jìn)行任務(wù)的處理
/* A block to be invoked when resume is invoked.
The block will be invoked even when the method is invoked on an ancestor of the receiver,
or an instance of NSProgress in another process that resulted from publishing the receiver or an ancestor of the receiver.
Your block won't be invoked on any particular queue.
If it must do work on a specific queue then it should schedule that work on that queue.
*/
@property (nullable, copy) void (^resumingHandler)(void) NS_AVAILABLE(10_11, 9_0);
調(diào)用resume時(shí)要調(diào)用的塊挽拂。 即使該方法在接收方的super類上調(diào)用惭每,或者由于發(fā)布接收方或接收方的super類而導(dǎo)致的另一個(gè)進(jìn)程中的NSProgress實(shí)例,也會(huì)調(diào)用該block亏栈。 您的塊不會(huì)在任何特定隊(duì)列上調(diào)用台腥。 如果它必須在特定的隊(duì)列上工作,那么它應(yīng)該在該隊(duì)列上安排該工作绒北。
2. 下載進(jìn)度
首先看一下這部分的代碼
[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];
}];
}
和上傳一樣黎侈,包括取消,暫停和開始闷游,下面我們就一起看一下峻汉。
(a) 取消
主要對(duì)應(yīng)下面幾句代碼
[self.downloadProgress setCancellable:YES];
[self.downloadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
這個(gè)不多說了贴汪,對(duì)比上傳的取消。
(b) 暫停
主要對(duì)應(yīng)下面幾句代碼
[self.downloadProgress setPausable:YES];
[self.downloadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
這個(gè)不多說了休吠,對(duì)比上傳的暫停扳埂。
(c) 開始
主要對(duì)應(yīng)下面這幾句代碼
if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.downloadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}
這個(gè)不多說了,對(duì)比上傳的開始瘤礁。
3. 給Task和上傳下載進(jìn)度增加KVO觀察
主要對(duì)應(yīng)下面這幾句代碼
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.uploadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
都是self阳懂,也就是AFURLSessionManager
,來觀察NSURLSessionTask
的四個(gè)屬性countOfBytesReceived柜思、countOfBytesSent岩调、countOfBytesExpectedToSend、countOfBytesExpectedToReceive
以及進(jìn)度的fractionCompleted屬性赡盘。
/* Byte count properties may be zero if no body is expected,
* or NSURLSessionTransferSizeUnknown if it is not possible
* to know how many bytes will be transferred.
*/
/* number of body bytes already received */
@property (readonly) int64_t countOfBytesReceived;
/* number of body bytes already sent */
@property (readonly) int64_t countOfBytesSent;
/* number of body bytes we expect to send, derived from the Content-Length of the HTTP request */
@property (readonly) int64_t countOfBytesExpectedToSend;
/* number of byte bytes we expect to receive, usually derived from the Content-Length header of an HTTP response. */
@property (readonly) int64_t countOfBytesExpectedToReceive;
/* The fraction of the overall work completed by this progress object, including work done by any children it may have.
*/
@property (readonly) double fractionCompleted;
此進(jìn)度對(duì)象完成的全部工作的一小部分号枕,包括可能有的任何子節(jié)點(diǎn)所做的工作。
下面我們就一起看一下KVO的監(jiān)聽部分亡脑。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
}
}
else if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
這里堕澄,首先判斷object的類型,如果是NSURLSessionTask
或者NSURLSessionDownloadTask
霉咨,然后判斷的是keyPath蛙紫,如果是對(duì)應(yīng)屬性或者鍵路徑,那么就更新downloadProgress
或者uploadProgress
的幾個(gè)對(duì)應(yīng)屬性的值途戒。
如果object是downloadProgress
坑傅,那么就調(diào)用block AFURLSessionTaskProgressBlock
。
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
else if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
如果object是uploadProgress
喷斋,那么就調(diào)用block AFURLSessionTaskProgressBlock
唁毒。
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
大家可以看到,這上傳和下載的block都是同一個(gè)類型的block星爪,它們是不同的實(shí)例對(duì)象而已浆西。
AFURLSessionManager為任務(wù)添加通知監(jiān)聽
上一篇講述過,添加通知監(jiān)聽如下:
[self addNotificationObserverForTask:task];
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}
1. 開始
- (NSString *)taskDescriptionForSessionTasks {
return [NSString stringWithFormat:@"%p", self];
}
- (void)taskDidResume:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}
一起來看一下這里的邏輯顽腾,首先就是取出任務(wù)notification.object
近零,然后判斷[task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]
,這里self.taskDescriptionForSessionTasks
的意思就是當(dāng)前實(shí)例化對(duì)象的地址抄肖。判斷如果是YES久信,那么就在主線程發(fā)送通知。
//通知名字
NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
2. 暫停
- (void)taskDidSuspend:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
});
}
}
}
暫停的邏輯參考開始漓摩,不同的地方就在于發(fā)送通知的name不一樣而已裙士。
NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
具體,這兩個(gè)通知有什么用管毙,誰監(jiān)聽做什么腿椎,后面會(huì)和大家進(jìn)行說明桌硫。
后記
本篇主要講述的就是lock之內(nèi)所做的事情,主要包括
AFURLSessionManagerTaskDelegate
代理為任務(wù)設(shè)置進(jìn)度和AFURLSessionManager
為任務(wù)添加通知監(jiān)聽酥诽。后面會(huì)繼續(xù)鞍泉,喜歡的給個(gè)關(guān)注~~~。