本篇是AFNetworking 3.0源碼解讀的第五篇了。
AFNetworking 3.0 源碼解讀(一)之 AFNetworkReachabilityManager
AFNetworking 3.0 源碼解讀(二)之 AFSecurityPolicy
AFNetworking 3.0 源碼解讀(三)之 AFURLRequestSerialization
AFNetworking 3.0 源碼解讀(四)之 AFURLResponseSerialization
這次主要介紹AFURLSessionManager這個(gè)類了。下一篇會(huì)介紹 AFHTTPSessionManager 。它是AFURLSessionManager的一個(gè)子類。
其實(shí),AFURLSessionManager 創(chuàng)建并管理著NSURLSession這個(gè)對(duì)象。而NSURLSession又基于NSURLSessionConfiguration。
AFURLSessionManager實(shí)現(xiàn)了四個(gè)協(xié)議:
1.NSURLSessionDelegate
URLSession:didBecomeInvalidWithError:
URLSession:didReceiveChallenge:completionHandler:
URLSessionDidFinishEventsForBackgroundURLSession:
2. NSURLSessionTaskDelegate
URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
URLSession:task:didReceiveChallenge:completionHandler:
URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
URLSession:task:needNewBodyStream:
URLSession:task:didCompleteWithError:
3. NSURLSessionDataDelegate
URLSession:dataTask:didReceiveResponse:completionHandler:
URLSession:dataTask:didBecomeDownloadTask:
URLSession:dataTask:didReceiveData:
URLSession:dataTask:willCacheResponse:completionHandler:
4. NSURLSessionDownloadDelegate
URLSession:downloadTask:didFinishDownloadingToURL:
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
上邊的這些協(xié)議方法在實(shí)現(xiàn)部分都會(huì)介紹到,如果自己寫的子類中重寫了這些代理方法,一定要調(diào)用[super xxx]。這篇也會(huì)很長(zhǎng),本來(lái)打算差分成兩個(gè)篇幅的,先因?yàn)榉椒ù蠖急容^榮日理解,最終決定還是放在一篇中比較好理解。
我們對(duì)AFURLSessionManager的頭文件做一個(gè)介紹:
1.@property(readonly,nonatomic,strong)NSURLSession*session;
關(guān)于NSURLSession的介紹,可以參考官方的文檔,文檔中提出,相對(duì)于NSURLConnection ,NSURLSession強(qiáng)大的功能是支持后臺(tái)上傳和下載。不過(guò)值得注意的是,這個(gè)對(duì)象與它的delegate之間的是一個(gè)強(qiáng)引用關(guān)系,因此在釋放NSURLSession時(shí),要做好處理。
在網(wǎng)上看到了這篇文章,使用NSURLSession,可以說(shuō)大體的講了NSURLSession的用法,不過(guò)我更喜歡開(kāi)頭的那首詩(shī)。
有的程序員老了,還沒(méi)聽(tīng)過(guò)NSURLSession
有的程序員還嫩,沒(méi)用過(guò)NSURLConnection
有的程序員很單純,他只知道AFN.
2.@property(readonly,nonatomic,strong)NSOperationQueue*operationQueue;
為NSURLSession 綁定一個(gè)隊(duì)列。并且設(shè)置這個(gè)隊(duì)列的最大并發(fā)數(shù)maxConcurrentOperationCount為1.
3.@property(nonatomic,strong)id responseSerializer;
這是序列化響應(yīng)數(shù)據(jù)的對(duì)象,默認(rèn)的模式是AFJSONResponseSerializer,而且不能為空。
4.@property(nonatomic,strong) AFSecurityPolicy *securityPolicy;
安全策略,默認(rèn)是defaultPolicy。
5.@property(readwrite,nonatomic,strong) AFNetworkReachabilityManager *reachabilityManager;
網(wǎng)絡(luò)監(jiān)控管理者。
跟獲取會(huì)話任務(wù)相關(guān)的屬性:
6.@property(readonly,nonatomic,strong)NSArray *tasks;
當(dāng)前被管理的包括data upload download 的任務(wù)的集合
7.@property(readonly,nonatomic,strong)NSArray *dataTasks;
當(dāng)前 data 的任務(wù)集合
8.@property(readonly,nonatomic,strong)NSArray *uploadTasks;
當(dāng)前 upload 的任務(wù)集合
9.@property(readonly,nonatomic,strong)NSArray *downloadTasks;
當(dāng)前 download 的任務(wù)集合。
回調(diào)的隊(duì)列
10.@property(nonatomic,strong, nullable)dispatch_queue_tcompletionQueue;
請(qǐng)求成功后,回調(diào)block會(huì)在這個(gè)隊(duì)列中調(diào)用,如果為空,就在主隊(duì)列。
11.@property(nonatomic,strong, nullable) dispatch_group_t completionGroup;
請(qǐng)求成功后,回調(diào)block會(huì)在這個(gè)組中調(diào)用,如果為空,就使用一個(gè)私有的。
修復(fù)后臺(tái)操作的bug
12.@property(nonatomic,assign)BOOLattemptsToRecreateUploadTasksForBackgroundSessions;
這個(gè)屬性用來(lái)解決在后臺(tái)創(chuàng)建上傳任務(wù)返回nil的bug,默認(rèn)為NO,如果設(shè)為YES,在后臺(tái)創(chuàng)建上傳任務(wù)失敗會(huì),會(huì)嘗試重新創(chuàng)建該任務(wù)。
初始化相關(guān)
13.-(instancetype)initWithSessionConfiguration:(nullableNSURLSessionConfiguration*)configurationNS_DESIGNATED_INITIALIZER;
這個(gè)方法是指定的初始化方法。那么什么叫指定的呢?
NS_DESIGNATED_INITIALIZER
這個(gè)宏告訴開(kāi)發(fā)者,如果寫一個(gè)集成A類的子類B,那么就要調(diào)用父類A的制定的初始化方法。舉個(gè)例子:
@interfaceMyClass:NSObject@property(copy,nonatomic)NSString*name;-(instancetype)initWithName:(NSString*)nameNS_DESIGNATED_INITIALIZER;-(instancetype)init;@end
如果我集成了MyClass而沒(méi)有時(shí)間initWithName: 方法,就會(huì)收到一個(gè)警告信息。點(diǎn)擊這里查看詳細(xì)信息
14.-(void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
根據(jù)是否取消未完成的任務(wù)來(lái)是session失效。
NSURLSession有兩個(gè)方法:
-(void)finishTasksAndInvalidate; 標(biāo)示待完成所有的任務(wù)后失效
-(void)invalidateAndCancel; 標(biāo)示 立即失效,未完成的任務(wù)也將結(jié)束
NSURLSessionDataTask
15.- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;16.- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uploadProgress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? downloadProgress:(nullablevoid(^)(NSProgress*downloadProgress))downloadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;
上邊的這兩個(gè)方法是和DataTask 相關(guān)的方法。
NSURLSessionUploadTask
17.- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fromFile:(NSURL*)fileURL? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;18.- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fromData:(nullableNSData*)bodyData? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;19.- (NSURLSessionUploadTask*)uploadTaskWithStreamedRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;
上邊的這三個(gè)方法是和UploadTask 相關(guān)的方法。分別對(duì)應(yīng)fileURL/data/request 這三種不同的數(shù)據(jù)源。
NSURLSessionDownloadTask
20.- (NSURLSessionDownloadTask*)downloadTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*downloadProgress))downloadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? destination:(nullableNSURL* (^)(NSURL*targetPath,NSURLResponse*response))destination? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,NSURL* _Nullable filePath,NSError* _Nullable error))completionHandler;21.- (NSURLSessionDownloadTask*)downloadTaskWithResumeData:(NSData*)resumeData? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*downloadProgress))downloadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? destination:(nullableNSURL* (^)(NSURL*targetPath,NSURLResponse*response))destination? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,NSURL* _Nullable filePath,NSError* _Nullable error))completionHandler;
上邊的這兩個(gè)方法是和DownloadTask 相關(guān)的方法。
頭文件中剩余的方法就是跟最開(kāi)始給出的代理有關(guān)了,讓我們能夠通過(guò)Block來(lái)處理那些代理的事件。在這就不做介紹了。
在這里再羅列出使用這個(gè)類中用到的通知:
AFNetworkingTaskDidResumeNotification
AFNetworkingTaskDidCompleteNotification
AFNetworkingTaskDidSuspendNotification
AFURLSessionDidInvalidateNotification
AFURLSessionDownloadTaskDidFailToMoveFileNotification
AFNetworkingTaskDidCompleteResponseDataKey
AFNetworkingTaskDidCompleteSerializedResponseKey
AFNetworkingTaskDidCompleteResponseSerializerKey
AFNetworkingTaskDidCompleteAssetPathKey
AFNetworkingTaskDidCompleteErrorKey
** 通過(guò)Block和通知,我們就有能力接收到跟網(wǎng)絡(luò)請(qǐng)求先關(guān)的事件和數(shù)據(jù)。也就是我們可以使用這些來(lái)處理我們的業(yè)務(wù)邏輯。**
我們現(xiàn)在來(lái)看.m文件的內(nèi)容
#ifndefNSFoundationVersionNumber_iOS_8_0#defineNSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11#else#defineNSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0#endif
上邊的這個(gè)宏的目的是通過(guò)NSFoundation的版本來(lái)判斷當(dāng)前ios版本,關(guān)鍵是這個(gè)宏的調(diào)試目標(biāo)是IOS,來(lái)看看系統(tǒng)是怎么定義的:
那么我們就能夠聯(lián)想到,目前我們能夠判斷系統(tǒng)版本號(hào)的方法有幾種呢?最少三種:
[UIDevice currentDevice].systemVersion
通過(guò)比較Foundation框架的版本號(hào),iOS系統(tǒng)升級(jí)的同時(shí)Foundation框架的版本也會(huì)提高
通過(guò)在某系版本中新出現(xiàn)的方法來(lái)判斷,UIAlertController 這個(gè)類是iOS8之后才出現(xiàn)的 NS_CLASS_AVAILABLE_IOS(8_0),如果當(dāng)前系統(tǒng)版本沒(méi)有這個(gè)類NSClassFromString(@"UIAlertController" == (null),從而判斷當(dāng)前版本是否大于等于iOS8
這篇博文寫的很詳細(xì),關(guān)于獲取當(dāng)前版本
staticdispatch_queue_turl_session_manager_creation_queue() {staticdispatch_queue_taf_url_session_manager_creation_queue;staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{? ? ? ? af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);? ? });returnaf_url_session_manager_creation_queue;}
AFNetworking中所有的和創(chuàng)建任務(wù)相關(guān)的事件都放到了一個(gè)單例的隊(duì)列中,我們平時(shí)可能會(huì)使用這些方法,但還是可能會(huì)忽略一些內(nèi)容,dispatch_queue_create()這個(gè)是隊(duì)列的方法,第一個(gè)參數(shù)是隊(duì)列的identifier,第二個(gè)參數(shù)則表示這個(gè)隊(duì)列是串行隊(duì)列還是并行隊(duì)列。
如果第二個(gè)參數(shù)為DISPATCH_QUEUE_SERIAL或NULL則表示隊(duì)列為串行隊(duì)列。如果為DISPATCH_QUEUE_CONCURRENT則表示是并行隊(duì)列。
關(guān)于隊(duì)列的小的知識(shí)點(diǎn),參考了這篇文章:Objective C 高級(jí)進(jìn)階— GCD隊(duì)列淺析(一).
staticvoidurl_session_manager_create_task_safely(dispatch_block_t block) {if(NSFoundationVersionNumber
再看這個(gè)方法,看名字能夠知道這應(yīng)該是一個(gè)安全創(chuàng)建人物的方法,那么我們會(huì)很疑惑,為什么創(chuàng)建人物要是安全的呢?難道我們按照順序創(chuàng)建人物,根據(jù)各自的Block回調(diào)處理事件會(huì)有問(wèn)題?? 是的,按照https://github.com/AFNetworking/AFNetworking/issues/2093這個(gè)的描述:
加入我們創(chuàng)建了一個(gè)人物task1對(duì)應(yīng)completionHandler1,然后又創(chuàng)建了task2對(duì)應(yīng)的completionHandler2,這時(shí)候在task2數(shù)據(jù)還沒(méi)有返回的前提下,task1的數(shù)據(jù)返回了,就會(huì)調(diào)用completionHandler2,就是這樣的一個(gè)bug,造成任務(wù)的創(chuàng)建是不安全的,不過(guò)這個(gè)問(wèn)題已經(jīng)在ios8后修復(fù)了。
這個(gè)方法還有一個(gè)小知識(shí)點(diǎn):dispatch_block_t,點(diǎn)擊去可以看到:
typedefvoid(^dispatch_block_t)(void);
關(guān)于這個(gè)Block我們應(yīng)該注意幾點(diǎn):
非ARC情況下,Block被allocated或者copied到堆后,一定要記得釋放它,通過(guò)[release]或者Block_release()
聲明Block時(shí),它是被分配到棧上的,要使用他,需要copy到堆才安全,因?yàn)闂?nèi)存是系統(tǒng)管理的,隨時(shí)可能被釋放。
staticdispatch_queue_turl_session_manager_processing_queue() {staticdispatch_queue_taf_url_session_manager_processing_queue;staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{? ? ? ? af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);? ? });returnaf_url_session_manager_processing_queue;}
這個(gè)方法是創(chuàng)建一個(gè)隊(duì)列用來(lái)管理數(shù)據(jù)的處理。和上邊的創(chuàng)建的方法對(duì)比,這個(gè)方法創(chuàng)建的隊(duì)列是一個(gè)并行的隊(duì)列,這就加快了數(shù)據(jù)的處理速度。
staticdispatch_group_turl_session_manager_completion_group(){staticdispatch_group_taf_url_session_manager_completion_group;staticdispatch_once_tonceToken;? ? dispatch_once(&onceToken, ^{? ? ? ? af_url_session_manager_completion_group = dispatch_group_create();? ? });returnaf_url_session_manager_completion_group;}
typedefNSInputStream* (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession*session,NSURLSessionTask*task);
大概講一下這個(gè)的使用方法,其實(shí)這行代碼的目的就是給一個(gè)Block定義一個(gè)名稱,在AFNEtworking中后邊的代碼,在使用這個(gè)Block的時(shí)候,就這么使用
@property(readwrite,nonatomic,copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
AFURLSessionManagerTaskDelegate
這個(gè)代理對(duì)象的目的是:
處理上傳或下載的進(jìn)度
處理獲取完數(shù)據(jù)后的行為
看這些屬性,我們需要了解的是NSProgress這個(gè)類,這個(gè)類是apple為了管理進(jìn)度在ios7新增的類。我們?cè)趇os開(kāi)發(fā)中,但凡使用到跟進(jìn)度相關(guān)的功能時(shí),應(yīng)盡量考慮始終它。它內(nèi)部是使用kvo機(jī)制監(jiān)聽(tīng)進(jìn)度的。
點(diǎn)擊了解更多NSProgress(English)
- (instancetype)init {self= [superinit];if(!self) {returnnil;? ? }self.mutableData = [NSMutableDatadata];self.uploadProgress = [[NSProgressalloc] initWithParent:niluserInfo:nil];self.uploadProgress.totalUnitCount =NSURLSessionTransferSizeUnknown;self.downloadProgress = [[NSProgressalloc] initWithParent:niluserInfo:nil];self.downloadProgress.totalUnitCount =NSURLSessionTransferSizeUnknown;returnself;}
來(lái)看看如何把task和進(jìn)度綁定在一起
- (void)setupProgressForTask:(NSURLSessionTask*)task {__weak__typeof__(task) weakTask = task;// 設(shè)置進(jìn)度的總單元數(shù)self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;// 設(shè)置上傳為可取消的[self.uploadProgress setCancellable:YES];[self.uploadProgress setCancellationHandler:^{? ? __typeof__(weakTask) strongTask = weakTask;? ? [strongTask cancel];}];// 設(shè)置上傳為可暫停的[self.uploadProgress setPausable:YES];[self.uploadProgress setPausingHandler:^{? ? __typeof__(weakTask) strongTask = weakTask;? ? [strongTask suspend];}];// 設(shè)置重新開(kāi)始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:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];[task addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];[task addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesSent))? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];[task addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];[self.downloadProgress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))? ? ? ? ? ? ? ? ? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];[self.uploadProgress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))? ? ? ? ? ? ? ? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];}
這個(gè)方法很長(zhǎng),但是也很簡(jiǎn)單,通過(guò)task.countOfBytesExpectedToSend能夠獲取到發(fā)送數(shù)據(jù)的總大小,通過(guò)task.countOfBytesExpectedToReceive能夠獲取到下載數(shù)據(jù)的總大小。
NSProgress 通過(guò)監(jiān)聽(tīng)fractionCompleted這個(gè)屬性來(lái)獲取進(jìn)度。
注意:在寫監(jiān)聽(tīng)方法的時(shí)候,這個(gè)options使用了NSKeyValueObservingOptionNew,代表什么意思呢?
點(diǎn)擊去后看到是一個(gè)NSKeyValueObservingOptions的枚舉:
NSKeyValueObservingOptionNew 把更改之前的值提供給處理方法
NSKeyValueObservingOptionOld 把更改之后的值提供給處理方法
NSKeyValueObservingOptionInitial 把初始化的值提供給處理方法,一旦注冊(cè),立馬就會(huì)調(diào)用一次。通常它會(huì)帶有新值,而不會(huì)帶有舊值
NSKeyValueObservingOptionPrior 分2次調(diào)用。在值改變之前和值改變之后
--
// 取消監(jiān)聽(tīng)- (void)cleanUpProgressForTask:(NSURLSessionTask*)task {? ? [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];? ? [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];? ? [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];? ? [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];? ? [self.downloadProgress removeObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))];? ? [self.uploadProgress removeObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))];}
--
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context {if([object isKindOfClass:[NSURLSessionTaskclass]] || [object isKindOfClass:[NSURLSessionDownloadTaskclass]]) {if([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];? ? ? ? }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];? ? ? ? }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];? ? ? ? }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];? ? ? ? }? ? }elseif([object isEqual:self.downloadProgress]) {if(self.downloadProgressBlock) {self.downloadProgressBlock(object);? ? ? ? }? ? }elseif([object isEqual:self.uploadProgress]) {if(self.uploadProgressBlock) {self.uploadProgressBlock(object);? ? ? ? }? ? }}
在這里要說(shuō)一下關(guān)于task四個(gè)代理的調(diào)用問(wèn)題。
task一共有4個(gè)delegate,只要設(shè)置了一個(gè),就代表四個(gè)全部設(shè)置,有時(shí)候一些delegate不會(huì)被觸發(fā)的原因在于這四種delegate是針對(duì)不同的URLSession類型和URLSessionTask類型來(lái)進(jìn)行響應(yīng)的,也就是說(shuō)不同的類型只會(huì)觸發(fā)這些delegate中的一部分,而不是觸發(fā)所有的delegate。
舉例說(shuō)明如下
觸發(fā)NSURLSessionDataDelegate
//使用函數(shù)dataTask來(lái)接收數(shù)據(jù)-(void)URLSession:(NSURLSession*)session dataTask:(NSURLSessionDataTask*)dataTask didReceiveData:(NSData*)data//則NSURLSession部分的代碼如下NSURLSessionConfiguration* ephConfiguration=[NSURLSessionConfigurationdefaultSessionConfiguration];NSURLSession* session=[NSURLSessionsessionWithConfiguration:ephConfiguration delegate:selfdelegateQueue:[NSOperationQueuemainQueue]];NSURL* url=[NSURLURLWithString:@"http://www.example.com/external_links/01.png"];NSURLSessionDataTask* dataTask=[session dataTaskWithURL:url];[dataTask resume];
觸發(fā)NSURLSessionDownloadDelegate
//使用函數(shù)downloadTask來(lái)接受數(shù)據(jù)-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL*)location//則NSURLSession部分的代碼如下NSURLSessionConfiguration* ephConfiguration=[NSURLSessionConfigurationdefaultSessionConfiguration];NSURLSession* session=[NSURLSessionsessionWithConfiguration:ephConfiguration delegate:selfdelegateQueue:[NSOperationQueuemainQueue]];NSURL* url=[NSURLURLWithString:@"http://www.example.com/external_links/01.png"];NSURLSessionDownloadTask* dataTask=[session downloadTaskWithURL:url];[dataTask resume];
這兩段代碼的主要區(qū)別在于NSURLSessionTask的類型的不同,造成了不同的Delegate被觸發(fā).
#pragma mark - NSURLSessionDataTaskDelegate- (void)URLSession:(__unusedNSURLSession*)session? ? ? ? ? dataTask:(__unusedNSURLSessionDataTask*)dataTask? ? didReceiveData:(NSData*)data{? ? [self.mutableData appendData:data];}
--
#pragma mark - NSURLSessionTaskDelegate- (void)URLSession:(__unusedNSURLSession*)session? ? ? ? ? ? ? task:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error{#pragma clang diagnostic push#pragma clang diagnostic ignored"-Wgnu"__strongAFURLSessionManager *manager =self.manager;? ? __blockidresponseObject =nil;// 使用字典來(lái)存放請(qǐng)求的結(jié)果__blockNSMutableDictionary*userInfo = [NSMutableDictionarydictionary];? ? userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;//Performance Improvement from #2672NSData*data =nil;if(self.mutableData) {? ? ? ? data = [self.mutableDatacopy];//We no longer need the reference, so nil it out to gain back some memory.self.mutableData =nil;? ? }//判斷是否有downloadFileURLif(self.downloadFileURL) {? ? ? ? userInfo[AFNetworkingTaskDidCompleteAssetPathKey] =self.downloadFileURL;? ? }elseif(data) {? ? ? ? userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;? ? }if(error) {? ? ? ? userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;? ? ? ? dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{if(self.completionHandler) {self.completionHandler(task.response, responseObject, error);? ? ? ? ? ? }// 發(fā)送通知dispatch_async(dispatch_get_main_queue(), ^{? ? ? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];? ? ? ? ? ? });? ? ? ? });? ? }else{dispatch_async(url_session_manager_processing_queue(), ^{NSError*serializationError =nil;// 解析數(shù)據(jù)responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];if(self.downloadFileURL) {? ? ? ? ? ? ? ? responseObject =self.downloadFileURL;? ? ? ? ? ? }if(responseObject) {? ? ? ? ? ? ? ? userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;? ? ? ? ? ? }if(serializationError) {? ? ? ? ? ? ? ? userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;? ? ? ? ? ? }? ? ? ? ? ? dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{if(self.completionHandler) {self.completionHandler(task.response, responseObject, serializationError);? ? ? ? ? ? ? ? }dispatch_async(dispatch_get_main_queue(), ^{? ? ? ? ? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];? ? ? ? ? ? ? ? });? ? ? ? ? ? });? ? ? ? });? ? }#pragma clang diagnostic pop}
這個(gè)方法是獲取數(shù)據(jù)完成了方法。最終通過(guò)self.completionHandler和** [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];**這兩個(gè)手段來(lái)傳遞數(shù)據(jù)和事件。
我們主要看看這個(gè)通知的userinfo會(huì)有那些信息:
AFNetworkingTaskDidCompleteResponseSerializerKey -> manager.responseSerializer
AFNetworkingTaskDidCompleteAssetPathKey -> self.downloadFileURL
AFNetworkingTaskDidCompleteResponseDataKey -> data
AFNetworkingTaskDidCompleteErrorKey -> error
AFNetworkingTaskDidCompleteSerializedResponseKey -> responseObject
--
#pragma mark - NSURLSessionDownloadTaskDelegate- (void)URLSession:(NSURLSession*)session? ? ? downloadTask:(NSURLSessionDownloadTask*)downloadTaskdidFinishDownloadingToURL:(NSURL*)location{NSError*fileManagerError =nil;self.downloadFileURL =nil;if(self.downloadTaskDidFinishDownloading) {self.downloadFileURL =self.downloadTaskDidFinishDownloading(session, downloadTask, location);if(self.downloadFileURL) {? ? ? ? ? ? [[NSFileManagerdefaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];if(fileManagerError) {? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];? ? ? ? }? ? ? }? ? }}
這個(gè)方法在下載完成后會(huì)調(diào)用。之前有一個(gè)使用場(chǎng)景,就是視頻邊下載邊播放。要求在視頻在下載完之前拿到正在下載的數(shù)據(jù)。ASI有一個(gè)屬性能夠拿到fileURL,AFNetworking卻沒(méi)有這個(gè)屬性,現(xiàn)在看來(lái),通過(guò)設(shè)置
(void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSessionsession, NSURLSessionDownloadTaskdownloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
可以把數(shù)據(jù)寫到一個(gè)我們定義的臨時(shí)的地方