我們先看一下AFNetworking.h文件都給了我們什么方法
#import <Foundation/Foundation.h>
AFNetWorking基本上是所有iOS項目的標(biāo)配×骄危現(xiàn)在升級帶最新版的3.X了。得益于蘋果從NSURLConnection升級到NSURLSession,AFN也實現(xiàn)了api的簡化草巡,同時功能卻一點沒少国拇。我們來看一下AFN3.X的目錄結(jié)構(gòu):
AFNetWorking 這個文件是一個頭文件。啥也沒做,就是引入了其他文件方便使用轧葛。
AFURLSessionManager 這個文件是核心類,基本上通過它來實現(xiàn)了大部分核心功能艇搀。負(fù)責(zé)請求的建立尿扯、管理、銷毀焰雕、安全衷笋、請求重定向、請求重啟等各種功能淀散。他主要實現(xiàn)了NSURLSession和NSRULSessionTask的封裝右莱。
AFHTTPSessionManager 這個文件是AFURLSessionManager的子類蚜锨。主要實現(xiàn)了對HTTP請求的優(yōu)化档插。
AFURLRequestSerialization 這個主要用于請求頭的編碼解碼、序列化亚再、優(yōu)化處理郭膛、簡化請求拼接過程等威彰。
AFURLResponseSerialization 這個主要用于網(wǎng)絡(luò)返回數(shù)據(jù)的序列化征绸、編碼解碼、序列化虚循、數(shù)據(jù)處理等如捅。
AFSecurityPolicy 這個主要用于請求的認(rèn)證功能棍现。比如https的認(rèn)證模式等。
AFNetworkReachabilityManager 這個主要用于監(jiān)聽網(wǎng)絡(luò)請求狀態(tài)變化功能镜遣。
首先說明己肮,看AFN源碼之前一定要搞清楚NSURLSession系列的api,這樣能讓你事半功倍悲关,具體可以看AFNetWorking源碼之NSRULSession系列概述谎僻。在這篇文章里,我們主要講解AFURLSessionManager的實現(xiàn)原理和封裝過程寓辱。首先我們通過一個簡單的網(wǎng)絡(luò)請求看一下他的基本用法(大部分都是非必須的艘绍,這里為了掩飾寫出來):
- (IBAction)clickButton:(id)sender {//通過默認(rèn)配置初始化SessionNSURLSessionConfiguration*configuration = [NSURLSessionConfigurationdefaultSessionConfiguration];? ? AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];//設(shè)置網(wǎng)絡(luò)請求序列化對象AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer];? ? [requestSerializer setValue:@"test"forHTTPHeaderField:@"requestHeader"];? ? requestSerializer.timeoutInterval =60;? ? requestSerializer.stringEncoding =NSUTF8StringEncoding;//設(shè)置返回數(shù)據(jù)序列化對象AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];? ? manager.responseSerializer = responseSerializer;//網(wǎng)絡(luò)請求安全策略if(true) {? ? ? ? AFSecurityPolicy *securityPolicy;? ? ? ? securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];? ? ? ? securityPolicy.allowInvalidCertificates =false;? ? ? ? securityPolicy.validatesDomainName =YES;? ? ? ? manager.securityPolicy = securityPolicy;? ? }else{? ? ? ? manager.securityPolicy.allowInvalidCertificates =true;? ? ? ? manager.securityPolicy.validatesDomainName =false;? ? }//是否允許請求重定向if(true) {? ? ? ? [manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest*(NSURLSession*session,NSURLSessionTask*task,NSURLResponse*response,NSURLRequest*request) {if(response) {returnnil;? ? ? ? ? ? }returnrequest;? ? ? ? }];? ? }//監(jiān)聽網(wǎng)絡(luò)狀態(tài)[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {NSLog(@"%ld",(long)status);? ? }];? ? [manager.reachabilityManager startMonitoring];NSURL*URL = [NSURLURLWithString:bigPic];NSURLRequest*request = [NSURLRequestrequestWithURL:URL];NSURLSessionDownloadTask*downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress*downloadProgress){NSLog(@"下載進(jìn)度:%lld",downloadProgress.completedUnitCount);? ? } destination:^NSURL*(NSURL*targetPath,NSURLResponse*response) {NSURL*documentsDirectoryURL = [[NSFileManagerdefaultManager] URLForDirectory:NSDocumentDirectoryinDomain:NSUserDomainMaskappropriateForURL:nilcreate:NOerror:nil];NSURL*fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];NSLog(@"fileURL:%@",[fileURL absoluteString]);returnfileURL;? ? } completionHandler:^(NSURLResponse*response,NSURL*filePath,NSError*error) {self.imageView.image = [UIImageimageWithData:[NSDatadataWithContentsOfURL:filePath]];NSLog(@"File downloaded to: %@", filePath);? ? }];? ? [downloadTask resume];}
通過這個請求,我們發(fā)現(xiàn)AFURLSessionManager要負(fù)責(zé)以下幾塊功能秫筏。
初始化和管理NSURLSession,通過它來建立和管理各種Task诱鞠。
初始化和管理NSRULSessionTask,通過不同task來發(fā)送不同請求挎挖。
管理各種認(rèn)證功能、安全功能航夺、請求重定向肋乍、數(shù)據(jù)處理。
管理和組織每個task的各種狀態(tài)管理和通知管理敷存。不同task的回調(diào)處理墓造。
幫我們管理和處理了NSRULSession系列api的各種代理方法。簡化了我們的處理锚烦。
2 AFURLSessionManager的聲明分析
AFURLSessionManager根據(jù)一個指定的NSURLSessionConfiguration創(chuàng)建和管理一個NSURLSession對象觅闽。并且這個對象實現(xiàn)了,,, 和這幾個協(xié)議的協(xié)議方法。同時實現(xiàn)NSSecureCoding和NSCopying來實現(xiàn)歸檔解檔和copy功能涮俄。
2.1AFURLSessionManager的初始化api
這些api主要用于初始化蛉拙、安全策略、網(wǎng)絡(luò)狀態(tài)監(jiān)聽等:
interface AFURLSessionManager :NSObject//指定的初始化方法彻亲、通過他來初始化一個Manager對象孕锄。- (instancetype)initWithSessionConfiguration:(nullableNSURLSessionConfiguration*)configuration//AFURLSessionManager通過session來管理和創(chuàng)建網(wǎng)絡(luò)請求。一個manager就實現(xiàn)了對這個session的管理苞尝,他們是一一對應(yīng)的關(guān)系畸肆。@property(readonly,nonatomic,strong)NSURLSession*session;//處理網(wǎng)絡(luò)請求回調(diào)的操作隊列,就是我們初始化session的時候傳入的那個OperationQueue參數(shù)。如果不傳入宙址,默認(rèn)是MainOperationQueue轴脐。@property(readonly,nonatomic,strong)NSOperationQueue*operationQueue;//對返回數(shù)據(jù)的處理都通過這個屬性來處理,比如數(shù)據(jù)的提取抡砂、轉(zhuǎn)換等大咱。默認(rèn)是一個`AFJSONResponseSerializer`對象用JSON的方式解析。@property(nonatomic,strong)id responseSerializer;//用于指定session的安全策略注益。用于處理信任主機(jī)和證書認(rèn)證等碴巾。默認(rèn)是`defaultPolicy`。@property(nonatomic,strong) AFSecurityPolicy *securityPolicy;//觀測網(wǎng)絡(luò)狀態(tài)的變化丑搔,具體可以看我的Demo用法厦瓢。@property(readwrite,nonatomic,strong) AFNetworkReachabilityManager *reachabilityManager;@end
2.2AFURLSessionManager獲取Task的api
這部分api主要是任務(wù)的創(chuàng)建、任務(wù)的分類低匙、任務(wù)完成隊列處理旷痕、特殊情況的任務(wù)重新創(chuàng)建等:
//當(dāng)前session創(chuàng)建的所有Task,這個是下面三種task的總和顽冶。@property(readonly,nonatomic,strong)NSArray *tasks;//當(dāng)前session創(chuàng)建的DataTask@property(readonly,nonatomic,strong)NSArray *dataTasks;//當(dāng)前session創(chuàng)建的uploadTask@property(readonly,nonatomic,strong)NSArray *uploadTasks;//當(dāng)前session創(chuàng)建的downloadTask@property(readonly,nonatomic,strong)NSArray *downloadTasks;//用于處理任務(wù)回調(diào)的GCD對象欺抗,默認(rèn)是dispatch_main_queue。@property(nonatomic,strong,nullable)dispatch_queue_tcompletionQueue;//用于處理任務(wù)回調(diào)的GCD的group對象强重,如果不初始化绞呈、則一個默認(rèn)的Group被使用贸人。@property(nonatomic,strong,nullable) dispatch_group_t completionGroup;//在iOS7的環(huán)境下,我們通過background模式的session創(chuàng)建的uploadTask有時會是nil佃声,如果這個屬性是yes艺智,AFN會嘗試再次創(chuàng)建uploadTask。@property(nonatomic,assign)BOOLattemptsToRecreateUploadTasksForBackgroundSessions;//廢除manager對應(yīng)的Session圾亏。通過傳入的參數(shù)來決定是否立即取消已經(jīng)用session發(fā)出去的任務(wù)十拣。- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
2.3AFURLSessionManager為管理Task創(chuàng)建Block
AFURLSessionManager提供了很多創(chuàng)建Task的api。并且提供了很多處理Task的Block志鹃。應(yīng)該說著幾個api就是AFN為我們提供的最大價值夭问,他把所有delegate方法細(xì)節(jié)都處理好。直接提供給我們一些最實用的api曹铃,我們就不用去管理session系列繁瑣的delegate方法了缰趋。
//創(chuàng)建一個NSURLSessionDataTask- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//創(chuàng)建一個NSURLSessionDataTask,并且能獲取上傳或者下載進(jìn)度- (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;//創(chuàng)建一個上傳Task,并且指定上傳文件的路徑陕见。- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fromFile:(NSURL*)fileURL? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;////創(chuàng)建一個上傳Task秘血,并且指定上傳的數(shù)據(jù)。- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fromData:(nullableNSData*)bodyData? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//創(chuàng)建一個uploadTask评甜,然后上傳數(shù)據(jù)- (NSURLSessionUploadTask*)uploadTaskWithStreamedRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//新建一個download任務(wù)灰粮,destination表示的下載文件的緩存路徑- (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;//繼續(xù)恢復(fù)一個download任務(wù)。resumeData參數(shù)表示的是恢復(fù)下載的時候初始化數(shù)據(jù)蜕着,比如前面已經(jīng)下載好的部分?jǐn)?shù)據(jù)谋竖。- (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;//獲取指定Task的上傳進(jìn)度- (nullableNSProgress*)uploadProgressForTask:(NSURLSessionTask*)task;//獲取指定Task的下載進(jìn)度- (nullableNSProgress*)downloadProgressForTask:(NSURLSessionTask*)task;
注意:上面所有Task的progress都不在主線程红柱、所以要在progress中做UI更新承匣,都必須手動在主線程操作。
2.4AFURLSessionManager設(shè)置各種情況的代理回調(diào)
這些回調(diào)Block主要是用于處理網(wǎng)絡(luò)請求過程或者結(jié)束以后的數(shù)據(jù)處理锤悄、認(rèn)證韧骗、通知、緩存等零聚。我們可以通過設(shè)置這些Block來獲取或者檢測各種狀態(tài)袍暴。相當(dāng)于就是鉤子函數(shù)。通過下面的這些Block隶症,我們基本可以獲取請求過程中的所有狀態(tài)以及需要做的各種處理政模。
//設(shè)置Session出錯或者無效的手的回調(diào)Block。這個Block主要在`NSURLSessionDelegate`代理的`URLSession:didBecomeInvalidWithError:`方法中執(zhí)行蚂会。- (void)setSessionDidBecomeInvalidBlock:(nullablevoid(^)(NSURLSession*session,NSError*error))block{? ? }//當(dāng)網(wǎng)絡(luò)請需要的認(rèn)證信息比如用戶名密碼已經(jīng)發(fā)送了的時候淋样,就可以通過這個Block來處理。這個Block是在`NSURLSessionDelegate`代理里面的`URLSession:didReceiveChallenge:completionHandler:`方法中被執(zhí)行胁住。注意這個是針對Session- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullableNSURLSessionAuthChallengeDisposition(^)(NSURLSession*session,NSURLAuthenticationChallenge*challenge,NSURLCredential* _Nullable __autoreleasing * _Nullable credential))block{? ? }////當(dāng)網(wǎng)絡(luò)請需要的認(rèn)證信息比如用戶名密碼已經(jīng)發(fā)送了的時候趁猴,就可以通過這個Block來處理刊咳。這個Block是在`NSURLSessionTaskDelegate`代理里面的`URLSession:task:didReceiveChallenge:completionHandler:`方法中被執(zhí)行。注意這個是針對Task儡司。- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullableNSURLSessionAuthChallengeDisposition(^)(NSURLSession*session,NSURLSessionTask*task,NSURLAuthenticationChallenge*challenge,NSURLCredential* _Nullable __autoreleasing * _Nullable credential))block{? ? }//當(dāng)請求需要一個新的bodystream的時候娱挨,就可以通過這個Block來設(shè)置。這個Block在`NSURLSessionTaskDelegate` 代理協(xié)議的`URLSession:task:needNewBodyStream:`方法里面設(shè)置捕犬。- (void)setTaskNeedNewBodyStreamBlock:(nullableNSInputStream* (^)(NSURLSession*session,NSURLSessionTask*task))block{? ? }//當(dāng)一個網(wǎng)絡(luò)請求需要重定向的時候跷坝。就會調(diào)用這個Block。這個Block是在`NSURLSessionTaskDelegate`協(xié)議的`URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`方法中調(diào)用的碉碉。- (void)setTaskWillPerformHTTPRedirectionBlock:(nullableNSURLRequest* (^)(NSURLSession*session,NSURLSessionTask*task,NSURLResponse*response,NSURLRequest*request))block{? ? }//可以通過設(shè)置這個Block來獲取上傳進(jìn)度探孝。這個Block主要在`NSURLSessionTaskDelegate`協(xié)議的 `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`方法中調(diào)用.- (void)setTaskDidSendBodyDataBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionTask*task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block{? ? }//設(shè)置一個Task完成以后執(zhí)行的Block,這個Block在`NSURLSessionTaskDelegate`協(xié)議的 `URLSession:task:didCompleteWithError:`方法中執(zhí)行誉裆。- (void)setTaskDidCompleteBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionTask*task,NSError* _Nullable error))block{? ? }//當(dāng)接收到網(wǎng)絡(luò)請求返回以后顿颅,可以調(diào)用這個Block。這個Block是在`NSURLSessionDataDelegate`協(xié)議的 `URLSession:dataTask:didReceiveResponse:completionHandler:`- (void)setDataTaskDidReceiveResponseBlock:(nullableNSURLSessionResponseDisposition(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSURLResponse*response))block{? ? }//如果一個dataTask轉(zhuǎn)換為downLoadTask以后足丢,就可以設(shè)置這個Block來調(diào)用粱腻。在`NSURLSessionDataDelegate` 協(xié)議的`URLSession:dataTask:didBecomeDownloadTask:`方法中調(diào)用。- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSURLSessionDownloadTask*downloadTask))block{? ? }//當(dāng)dataTask接收到數(shù)據(jù)以后斩跌,可以設(shè)置調(diào)用這個Block绍些。具體在`NSURLSessionDataDelegate`協(xié)議的`URLSession:dataTask:didReceiveData:`方法。- (void)setDataTaskDidReceiveDataBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSData*data))block{? ? }//設(shè)置一個Block來決定是否處理或者換成網(wǎng)絡(luò)請求緩存耀鸦。具體在`NSURLSessionDataDelegate`協(xié)議的`URLSession:dataTask:willCacheResponse:completionHandler:`方法中柬批。- (void)setDataTaskWillCacheResponseBlock:(nullableNSCachedURLResponse* (^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSCachedURLResponse*proposedResponse))block{? ? }//當(dāng)session所有的任務(wù)都發(fā)送出去以后,就可以通過這個Block來獲取袖订。具體在`NSURLSessionDataDelegate`協(xié)議的 `URLSessionDidFinishEventsForBackgroundURLSession:`方法中氮帐。- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullablevoid(^)(NSURLSession*session))block{? ? }//當(dāng)一個downloadTask執(zhí)行完畢以后,可以通過這個Block來獲取下載信息洛姑,我們可以通過這個Block獲取下載文件的位置上沐。具體在`NSURLSessionDownloadDelegate`協(xié)議的`URLSession:downloadTask:didFinishDownloadingToURL:`方法中被調(diào)用。- (void)setDownloadTaskDidFinishDownloadingBlock:(nullableNSURL* _Nullable? (^)(NSURLSession*session,NSURLSessionDownloadTask*downloadTask,NSURL*location))block{? ? }//可以通過這個Block獲取一個downloadTask的下載進(jìn)度楞艾。這個Block會在下載過程中多次被調(diào)用参咙。具體是在`NSURLSessionDownloadDelegate`協(xié)議中的`URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`方法中被調(diào)用。- (void)setDownloadTaskDidWriteDataBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDownloadTask*downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block{? ? }//當(dāng)一個downloadTask重新開始以后硫眯,我們可以通過這個Block獲取fileOffSet等信息獲取已經(jīng)下載的部分以及總共有多少要下載蕴侧。具體是在`NSURLSessionDownloadDelegate`協(xié)議的`URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`方法中被調(diào)用。- (void)setDownloadTaskDidResumeBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDownloadTask*downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block{? ? }
除了上面的部分两入,AFURLSessionManager的頭文件還提供了很多notification的聲明净宵。通過這些通知,我們可以獲取Task是否開始、是否完成塘娶、是否掛起归斤、是否無效等各種通知。具體可以去文件里看刁岸。
3 AFURLSessionManager的實現(xiàn)分析
AFURLSessionManager.m文件里面除了有AFURLSessionManager.h定義的各種接口的實現(xiàn)意外脏里,還有處理不同iOS版本下NSRULSession不同的部分,以及多個全局dispatch_queue_t的定義虹曙、以及處理NSURLSeesionTash的各種代理方法的實現(xiàn)和處理迫横。具體劃分如下:
NSURLSessionManager的實現(xiàn)。主要實現(xiàn)了接口文件定義的各種api的實現(xiàn)酝碳,比如Task的創(chuàng)建矾踱、Task的獲取、Task的各種代理方法的實現(xiàn)疏哗、NSCoping和NSCoding協(xié)議呛讲、以及各種Block的實現(xiàn)。
基本屬性的初始化返奉。比如sessionConfiguration贝搁、operationQueue、session芽偏、mutableTaskDelegatesKeyedByTaskIdentifier等屬性雷逆。以及用于實現(xiàn)task和AFURLSessionManagerTaskDelegate的綁定的taskDescriptionForSessionTasks、還有關(guān)鍵操作的鎖屬性lock污尉。
接口文件的各種Block對應(yīng)的屬性膀哲,一個Block對應(yīng)一個屬性。
處理Task暫停與重啟操作的方法被碗。
給Task設(shè)置AFURLSessionManagerTaskDelegate代理的方法某宪。
初始化Task的各種方法。
設(shè)置B接口文件定義的各種Block蛮放。
NSURLSession系列代理方法缩抡。
_AFURLSessionTaskSwizzling私有類。主要實現(xiàn)了iOS7和iOS8系統(tǒng)上NSURLSession差別的處理包颁。讓不同系統(tǒng)版本NSURLSession版本基本一致。
AFURLSessionManagerTaskDelegate這個類主要是把NSURLSeesion的部分代理方法讓他處理压真。從而達(dá)到簡化代碼的目的娩嚼。
處理Task的上傳或者下載進(jìn)度。
處理封裝NSURLSeesion返回的數(shù)據(jù)滴肿。
Task完成等的通知封裝岳悟。
全局dispatch_queue_t和dispatch_group_t的定義。各種通知名稱的初始化,各種Block的類型定義贵少。
3.1 AFURLSessionManager一個網(wǎng)絡(luò)請求實現(xiàn)過程
我們通過一個網(wǎng)絡(luò)請求過程來分析AFURLSessionManager.m的實現(xiàn)呵俏。我們通過initWithSessionConfiguration方法初始化一個manager。在這個方法里會初始化各種屬性滔灶、以及為session屬性設(shè)置代理:
接口文件中的代碼如下:
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]];
實現(xiàn)文件中對應(yīng)的處理如下:
/**
初始化方法
@return 返回一個manager對象
*/- (instancetype)init {return[selfinitWithSessionConfiguration:nil];}/**
默認(rèn)初始化方法普碎、通過這個方法來做manager的具體化初始化動作
@param configuration NSURLSession的配置
@return 返回一個manager對象
*/- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration*)configuration {self= [superinit];if(!self) {returnnil;? ? }//如果用戶沒有手動指定,則使用默認(rèn)的configuration來初始化if(!configuration) {? ? ? ? configuration = [NSURLSessionConfigurationdefaultSessionConfiguration];? ? }//賦值給屬性self.sessionConfiguration = configuration;//初始化NSURLSession的task代理方法執(zhí)行的隊列录平。//這里有一個很關(guān)鍵的點是task的代理執(zhí)行的queque一次性只能執(zhí)行一個task麻车。這樣就避免了task的代理方法執(zhí)行的混亂。self.operationQueue = [[NSOperationQueuealloc] init];self.operationQueue.maxConcurrentOperationCount =1;//出絲滑NSURLSession對象斗这,最核心的對象动猬。self.session = [NSURLSessionsessionWithConfiguration:self.sessionConfiguration delegate:selfdelegateQueue:self.operationQueue];//如果用戶沒有手動指定,則返回的數(shù)據(jù)是JSON格式序列化表箭。self.responseSerializer = [AFJSONResponseSerializer serializer];//指定https處理的安全策略赁咙。self.securityPolicy = [AFSecurityPolicy defaultPolicy];#if !TARGET_OS_WATCH//初始化網(wǎng)絡(luò)狀態(tài)監(jiān)聽屬性self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];#endif//用于記錄Task與他的`AFURLSessionManagerTaskDelegate`代理對象的一一對應(yīng)關(guān)系。通過這個self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionaryalloc] init];//初始化一個鎖對象,關(guān)鍵操作加鎖免钻。self.lock = [[NSLockalloc] init];self.lock.name = AFURLSessionManagerLockName;/**
? ? 獲取當(dāng)前session正在執(zhí)行的所有Task序目。同時為每一個Task添加`AFURLSessionManagerTaskDelegate`代理對象,這個代理對象主要用于管理uplaodTak和downloadTask的進(jìn)度管理伯襟。并且在Task執(zhí)行完畢以后調(diào)用相應(yīng)的Block猿涨。同時發(fā)送相應(yīng)的notification對象,實現(xiàn)對task數(shù)據(jù)或者狀態(tài)改變的檢測姆怪。
? ? @param dataTasks dataTask列表
? ? @param uploadTasks uplaodTask列表
? ? @param downloadTasks downloadTask列表
? ? @return
? ? */[self.session getTasksWithCompletionHandler:^(NSArray*dataTasks,NSArray*uploadTasks,NSArray*downloadTasks) {for(NSURLSessionDataTask*taskindataTasks) {? ? ? ? ? ? [selfaddDelegateForDataTask:task uploadProgress:nildownloadProgress:nilcompletionHandler:nil];? ? ? ? }for(NSURLSessionUploadTask*uploadTaskinuploadTasks) {? ? ? ? ? ? [selfaddDelegateForUploadTask:uploadTask progress:nilcompletionHandler:nil];? ? ? ? }for(NSURLSessionDownloadTask*downloadTaskindownloadTasks) {? ? ? ? ? ? [selfaddDelegateForDownloadTask:downloadTask progress:nildestination:nilcompletionHandler:nil];? ? ? ? }? ? }];returnself;}
請求執(zhí)行叛赚,接口文件如下:
NSURLSessionDownloadTask*downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress*downloadProgress){NSLog(@"下載進(jìn)度:%lld",downloadProgress.completedUnitCount);} destination:^NSURL*(NSURL*targetPath,NSURLResponse*response) {NSURL*documentsDirectoryURL = [[NSFileManagerdefaultManager] URLForDirectory:NSDocumentDirectoryinDomain:NSUserDomainMaskappropriateForURL:nilcreate:NOerror:nil];NSURL*fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];NSLog(@"fileURL:%@",[fileURL absoluteString]);returnfileURL;} completionHandler:^(NSURLResponse*response,NSURL*filePath,NSError*error) {self.imageView.image = [UIImageimageWithData:[NSDatadataWithContentsOfURL:filePath]];NSLog(@"File downloaded to: %@", filePath);}];
實現(xiàn)文件則調(diào)用了很多方法:
1 首先是初始化一個NSURLSessionDownLoadTask對象
//通過session創(chuàng)建一個downloadTask,__blockNSURLSessionDownloadTask*downloadTask =nil;//url_session_manager_create_task_safely作用是修復(fù)在iOS8下面的系統(tǒng)bug稽揭。url_session_manager_create_task_safely(^{? ? ? ? downloadTask = [self.session downloadTaskWithRequest:request];? ? });? ? [selfaddDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];returndownloadTask;
2 通過[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];這句話來為Task設(shè)置一個AFURLSessionManagerTaskDelegate代理對象俺附。從而可以實現(xiàn)對進(jìn)度處理、Block調(diào)用溪掀、Task完成返回數(shù)據(jù)的拼裝的功能事镣。
//根據(jù)指定的Task,初始化一個AFURLSessionManagerTaskDelegateAFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];? ? delegate.manager =self;//設(shè)置Task完成的回調(diào)Blockdelegate.completionHandler = completionHandler;if(destination) {//任務(wù)完成以后揪胃,調(diào)用destination這個Blockdelegate.downloadTaskDidFinishDownloading = ^NSURL* (NSURLSession* __unused session,NSURLSessionDownloadTask*task,NSURL*location) {returndestination(location, task.response);? ? ? ? };? ? }//指定Task與taskDescriptionForSessionTasks的關(guān)聯(lián)關(guān)系璃哟,方便后面的通知中做對應(yīng)的處理。downloadTask.taskDescription =self.taskDescriptionForSessionTasks;//添加通知[selfsetDelegate:delegate forTask:downloadTask];//設(shè)置一個下載進(jìn)度的Block喊递,以便在后面代理方法中調(diào)用随闪。delegate.downloadProgressBlock = downloadProgressBlock;
3 初始化一個AFURLSessionManagerTaskDelegate對象。在這個對象中對Task的請求過程進(jìn)行處理和控制骚勘。
/**
初始化一個AFURLSessionManagerTaskDelegate對象
@param task 對象綁定的Task
@return 返回對象
*/- (instancetype)initWithTask:(NSURLSessionTask*)task {self= [superinit];if(!self) {returnnil;? ? }//這個屬性用于存儲Task下載過程中的數(shù)據(jù)_mutableData = [NSMutableDatadata];//存儲Task上傳和下載的進(jìn)度_uploadProgress = [[NSProgressalloc] initWithParent:niluserInfo:nil];? ? _downloadProgress = [[NSProgressalloc] initWithParent:niluserInfo:nil];? ? __weak__typeof__(task) weakTask = task;for(NSProgress*progressin@[ _uploadProgress, _downloadProgress ])? ? {? ? ? ? progress.totalUnitCount =NSURLSessionTransferSizeUnknown;? ? ? ? progress.cancellable =YES;//當(dāng)progress對象取消的時候铐伴,取消Taskprogress.cancellationHandler = ^{? ? ? ? ? ? [weakTask cancel];? ? ? ? };? ? ? ? progress.pausable =YES;? ? ? ? progress.pausingHandler = ^{//掛起Task[weakTask suspend];? ? ? ? };if([progress respondsToSelector:@selector(setResumingHandler:)]) {? ? ? ? ? ? progress.resumingHandler = ^{//重啟Task[weakTask resume];? ? ? ? ? ? };? ? ? ? }//更具progress的進(jìn)度來獲取Task的進(jìn)度撮奏。fractionCompleted方法在請求過程中多次執(zhí)行。[progress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))? ? ? ? ? ? ? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];? ? }returnself;}//上面通過對fractionCompleted方法KVO当宴。則會調(diào)用下面的方法畜吊,從而執(zhí)行manager的- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context {if([object isEqual:self.downloadProgress]) {//更新下載進(jìn)度Blockif(self.downloadProgressBlock) {self.downloadProgressBlock(object);? ? ? ? }? ? }elseif([object isEqual:self.uploadProgress]) {//更新上傳進(jìn)度Blocif(self.uploadProgressBlock) {self.uploadProgressBlock(object);? ? ? ? }? ? }}
4 在AFURLSessionManagerTaskDelegate設(shè)置Task狀態(tài)改變的監(jiān)聽。
/**
設(shè)置指定task的`AFURLSessionManagerTaskDelegate`對象户矢。并且添加task掛起或者重啟的監(jiān)聽玲献。
@param delegate 代理對象
@param task task
*/- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate? ? ? ? ? ? forTask:(NSURLSessionTask*)task{NSParameterAssert(task);NSParameterAssert(delegate);//加鎖操作[self.lock lock];//為Task設(shè)置與之代理方法關(guān)聯(lián)關(guān)系。通過一個字典self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;//添加對Task開始逗嫡、重啟青自、掛起狀態(tài)的通知的接收。[selfaddNotificationObserverForTask:task];? ? [self.lock unlock];}/**
給Task添加任務(wù)開始驱证、重啟延窜、掛起的通知
@param task 任務(wù)
*/- (void)addNotificationObserverForTask:(NSURLSessionTask*)task {? ? [[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];? ? [[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];}
5 從下面開始,任務(wù)就正式開始執(zhí)行抹锄。其實就是[downloadTask resume];執(zhí)行以后開始逆瑞。
/**
在網(wǎng)絡(luò)請求正式開始以后,這個方法會在數(shù)據(jù)接收的過程中多次調(diào)用伙单。我們可以通過這個方法獲取數(shù)據(jù)下載的大小获高、總得大小、還有多少么有下載
@param session session
@param downloadTask 對應(yīng)的Task
@param bytesWritten 已經(jīng)下載的字節(jié)
@param totalBytesWritten 總的字節(jié)大小
@param totalBytesExpectedToWrite nil
*/- (void)URLSession:(NSURLSession*)session? ? ? downloadTask:(NSURLSessionDownloadTask*)downloadTask? ? ? didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{//獲取Task對應(yīng)的`AFURLSessionManagerTaskDelegate`對象吻育。從而可以調(diào)用對應(yīng)的代理方法AFURLSessionManagerTaskDelegate *delegate = [selfdelegateForTask:downloadTask];if(delegate) {//調(diào)用`AFURLSessionManagerTaskDelegate`類中的代理方法念秧。從而實現(xiàn)對于進(jìn)度更新等功能。//會調(diào)用下面的那個方法[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];? ? }if(self.downloadTaskDidWriteData) {//如果有`downloadTaskDidWriteData`Block的實現(xiàn)布疼,則在這個調(diào)用Block從而實現(xiàn)對下載進(jìn)度過程的控制摊趾。self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);? ? }}//AFURLSessionManagerTaskDelegate里面的這個代理方法實現(xiàn)對進(jìn)度的更新。- (void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask? ? ? didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{//AFURLSessionManagerTaskDelegate代理方法實現(xiàn)對下載進(jìn)度的記錄self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;self.downloadProgress.completedUnitCount = totalBytesWritten;}
6 Task完成以后游两,會調(diào)用AFURLSessionManagerTaskDelegate對象的方法對返回的數(shù)據(jù)封裝砾层。
//AFURLSessionManagerTaskDelegate里面的這個代理方法實現(xiàn)對數(shù)據(jù)的具體處理。- (void)URLSession:(__unusedNSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError*)error{//獲取Task對應(yīng)的manager對象__strongAFURLSessionManager *manager =self.manager;//要封裝的responseObject對象贱案。__blockidresponseObject =nil;? ? __blockNSMutableDictionary*userInfo = [NSMutableDictionarydictionary];? ? userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;//返回的數(shù)據(jù)肛炮。NSData*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;? ? }//如果是downloadTask,則封裝downloadFileURLif(self.downloadFileURL) {? ? ? ? userInfo[AFNetworkingTaskDidCompleteAssetPathKey] =self.downloadFileURL;? ? }elseif(data) {//如果是其他Task宝踪,則封裝返回的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(), ^{//如果Task有completionHandler。則調(diào)用這個Blockif(self.completionHandler) {self.completionHandler(task.response, responseObject, error);? ? ? ? ? ? }//發(fā)送一個指定Task結(jié)束的通知dispatch_async(dispatch_get_main_queue(), ^{? ? ? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];? ? ? ? ? ? });? ? ? ? });? ? }else{//正確數(shù)據(jù)封裝//在一個并行的dispat_queuq_t對象里面異步處理肴沫。dispatch_async(url_session_manager_processing_queue(), ^{NSError*serializationError =nil;//封裝responseBojctresponseObject = [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(), ^{//如果Task有完成Block粟害。則調(diào)用這個Blockif(self.completionHandler) {self.completionHandler(task.response, responseObject, serializationError);? ? ? ? ? ? ? ? }//發(fā)送通知dispatch_async(dispatch_get_main_queue(), ^{? ? ? ? ? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];? ? ? ? ? ? ? ? });? ? ? ? ? ? });? ? ? ? });? ? }}
7 移除Task對應(yīng)的通知和對應(yīng)的AFURLSessionManagerTaskDelegate代理對象。
- (void)removeDelegateForTask:(NSURLSessionTask*)task {NSParameterAssert(task);? ? [self.lock lock];//移除Task對應(yīng)的通知[selfremoveNotificationObserverForTask:task];//移除Task對應(yīng)的`AFURLSessionManagerTaskDelegate`代理對象颤芬。[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];? ? [self.lock unlock];}//移除通知監(jiān)聽- (void)removeNotificationObserverForTask:(NSURLSessionTask*)task {? ? [[NSNotificationCenterdefaultCenter] removeObserver:selfname:AFNSURLSessionTaskDidSuspendNotification object:task];? ? [[NSNotificationCenterdefaultCenter] removeObserver:selfname:AFNSURLSessionTaskDidResumeNotification object:task];}//`AFURLSessionManagerTaskDelegate`對象回收。- (void)dealloc {? ? [self.downloadProgress removeObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))];? ? [self.uploadProgress removeObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))];}
通過上面的過程,我們發(fā)現(xiàn)核心流程都是圍繞了NSRULSessionTask對象以及與之綁定的AFURLSessionManagerTaskDelegate對象執(zhí)行的站蝠。我們通過在NSRULSessionTask對象的代理方法里面手動調(diào)用AFURLSessionManagerTaskDelegate對應(yīng)的代理方法來實現(xiàn)對數(shù)據(jù)的處理和簡化代碼的作用汰具,這個設(shè)計思路的確吊吊的。還有一些方法沒有涉及到菱魔,不過大同小異留荔,基本過程就是這樣,就不一一解釋了澜倦。
3.2 AFURLSessionManager一些特殊模塊的說明
AFURLSeeesionManager實現(xiàn)了NSSecureCoding協(xié)議聚蝶。讓manager可以歸檔解檔。
/**
在iOS8以及以上環(huán)境下藻治,supportsSecureCoding必須重寫并且返回true碘勉。
@return bool
*/+ (BOOL)supportsSecureCoding {returnYES;}//解檔- (instancetype)initWithCoder:(NSCoder*)decoder {NSURLSessionConfiguration*configuration = [decoder decodeObjectOfClass:[NSURLSessionConfigurationclass] forKey:@"sessionConfiguration"];self= [selfinitWithSessionConfiguration:configuration];if(!self) {returnnil;? ? }returnself;}/**
我們發(fā)現(xiàn)對象歸檔的時候,只歸檔了`NSURLSessionConfiguration`屬性桩卵。所以說歸檔接檔的時候所有Block設(shè)置验靡、operation設(shè)置都會失效。
@param coder coder
*/- (void)encodeWithCoder:(NSCoder*)coder {? ? [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];}
同時雏节,AFURLSessionManager也實現(xiàn)了NSCopying協(xié)議胜嗓。通過協(xié)議的實現(xiàn)過程,我們發(fā)現(xiàn)也是只使用了NSURLSessionConfiguration屬性钩乍。和歸檔解檔一樣辞州。
#pragma mark - 實現(xiàn)NSCopying協(xié)議。copy的NAURLSessionManager沒有復(fù)制任何與代理處理相關(guān)的Block- (instancetype)copyWithZone:(NSZone*)zone {return[[[selfclass] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];}
有的時候寥粹,我們的請求會返回302這個狀態(tài)碼变过,這個表示需要請求重定向到另一個url,我們可以下面這個代理方法里面決定對于重定向的處理,如果對completionHandler傳入nil,則會把response傳入重定向請求排作。另外牵啦,backgroundSession的Task不會調(diào)用下面這個代理方法,而是直接調(diào)用妄痪。
/**
有的時候哈雏,我們的請求會返回302這個狀態(tài)碼,這個表示需要請求重定向到另一個url衫生,我們可以在這個代理方法里面絕定對于重定向的處理裳瘪。
@param session session
@param task task
@param response response
@param request 重定向的request。
@param completionHandler 請求完成
*/- (void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task willPerformHTTPRedirection:(NSHTTPURLResponse*)response newRequest:(NSURLRequest*)request completionHandler:(void(^)(NSURLRequest*))completionHandler{//重定向的request對象NSURLRequest*redirectRequest = request;//如果用戶指定了taskWillPerformHTTPRedirection這個Block,我們就通過這個Block的調(diào)用返回處理完成的request對象罪针。if(self.taskWillPerformHTTPRedirection) {? ? ? ? redirectRequest =self.taskWillPerformHTTPRedirection(session, task, response, request);? ? }//這個調(diào)用是必須的彭羹,執(zhí)行重定向操作。if(completionHandler) {? ? ? ? completionHandler(redirectRequest);? ? }}
創(chuàng)建NSRULSessionUplaodTask的時候泪酱,在某些系統(tǒng)上會出現(xiàn)bug派殷。AFN已經(jīng)幫我們處理好:
- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request fromFile:(NSURL*)fileURL progress:(void(^)(NSProgress*uploadProgress)) uploadProgressBlock completionHandler:(void(^)(NSURLResponse*response,idresponseObject,NSError*error))completionHandler{? ? __blockNSURLSessionUploadTask*uploadTask =nil;//用線程安全的方式創(chuàng)建一個dataTask还最。修復(fù)iOS8下面的bug。url_session_manager_create_task_safely(^{? ? ? ? uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];? ? });//用于處理uploadTask在iOS7環(huán)境下面有可能創(chuàng)建失敗的情況毡惜。如果attemptsToRecreateUploadTasksForBackgroundSessions為true拓轻。則嘗試重新創(chuàng)建Task。如果三次都沒有成功经伙,則放棄扶叉。if(!uploadTask &&self.attemptsToRecreateUploadTasksForBackgroundSessions &&self.session.configuration.identifier) {for(NSUIntegerattempts =0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {? ? ? ? ? ? uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];? ? ? ? }? ? }//為Task添加`AFURLSessionManagerTaskDelegate`代理方法[selfaddDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];returnuploadTask;}
通過使用dispatch_semaphore_t來控制對異步處理返回結(jié)果的控制。非常有借鑒意義帕膜。
#pragma mark -? 獲取當(dāng)前session對應(yīng)的task列表枣氧。通過dispatch_semaphore_t來控制訪問過程。- (NSArray*)tasksForKeyPath:(NSString*)keyPath {? ? __blockNSArray*tasks =nil;? ? dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);? ? [self.session getTasksWithCompletionHandler:^(NSArray*dataTasks,NSArray*uploadTasks,NSArray*downloadTasks) {if([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {? ? ? ? ? ? tasks = dataTasks;? ? ? ? }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {? ? ? ? ? ? tasks = uploadTasks;? ? ? ? }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {? ? ? ? ? ? tasks = downloadTasks;? ? ? ? }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {? ? ? ? ? ? tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];? ? ? ? }//這里發(fā)送一個信號量垮刹,讓semaphore變?yōu)?达吞。此時表示tasks已經(jīng)成功獲取。dispatch_semaphore_signal(semaphore);? ? }];//這里會一直等待信號量變?yōu)?危纫。dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//返回Task宗挥。通過信號量控制,避免了方法結(jié)束的時候种蝶,tasks還沒有正常獲取的情況契耿。returntasks;}
4 _AFURLSessionTaskSwizzling私有類的說明
在iOS7和iOS8及以上的系統(tǒng),NSRULSessionTask的具體實現(xiàn)是不同的螃征。我們目前知道的不同有:
NSURLSessionTasks是一個類簇搪桂。所以我們初始化一個Task的時候,我們并不只到初始化的到底是哪個子類盯滚。
簡單的通過[NSURLSessionTask class]并不會起作用踢械。必須通過NSURLSession創(chuàng)建一個task對象。然后獲取他所在的類魄藕。
iOS7下面,下面代碼中的localDataTask對象的繼承關(guān)系是__NSCFLocalDataTask->__NSCFLocalSessionTask->__NSCFURLSessionTask内列。
在iOS8以及以上系統(tǒng)。下面代碼中的localDataTask對象的繼承關(guān)系是__NSCFLocalDataTask->__NSCFLocalSessionTask->NSURLSessionTask背率。
在iOS7下面__NSCFLocalSessionTask和__NSCFURLSessionTask實現(xiàn)了resume和suspend方法话瞧,同時最重要的是他不調(diào)用父類的實現(xiàn)。但是iOS8下面寝姿,只有NSURLSessionTask實現(xiàn)了resume和suspend交排。所以在iOS7的環(huán)境下,我們需要想辦法讓resume和suspend調(diào)用NSURLSessionTask的具體實現(xiàn)饵筑。
下面的代碼完美的向我們展示了一個向類添加方法埃篓,并且swizzle方法實現(xiàn)的過程。值得仔細(xì)琢磨根资。
/**
切換theClass類的`originalSelector`和`swizzledSelector`的實現(xiàn)
@param theClass 類
@param originalSelector 方法一
@param swizzledSelector 方法2
*/staticinlinevoidaf_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {? ? Method originalMethod = class_getInstanceMethod(theClass, originalSelector);? ? Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);? ? method_exchangeImplementations(originalMethod, swizzledMethod);}/**
動態(tài)給一個類添加方法
@param theClass 類
@param selector 方法名字
@param method 方法體
@return bool
*/staticinlineBOOLaf_addMethod(Class theClass, SEL selector, Method method) {returnclass_addMethod(theClass, selector,? method_getImplementation(method),? method_getTypeEncoding(method));}@implementation_AFURLSessionTaskSwizzling+ (void)load {if(NSClassFromString(@"NSURLSessionTask")) {NSURLSessionConfiguration*configuration = [NSURLSessionConfigurationephemeralSessionConfiguration];NSURLSession* session = [NSURLSessionsessionWithConfiguration:configuration];#pragma GCC diagnostic push#pragma GCC diagnostic ignored"-Wnonnull"http://初始化一個dataTask對象NSURLSessionDataTask*localDataTask = [session dataTaskWithURL:nil];#pragma clang diagnostic pop//獲取af_resume這個方法的實現(xiàn)架专。IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([selfclass],@selector(af_resume)));//獲取dataTask的具體類Class currentClass = [localDataTaskclass];//如果父類有resume方法同窘。則改變方法的具體實現(xiàn)。while(class_getInstanceMethod(currentClass,@selector(resume))) {? ? ? ? ? ? Class superClass = [currentClass superclass];//找到類和父類的resume方法實現(xiàn)IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass,@selector(resume)));? ? ? ? ? ? IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass,@selector(resume)));if(classResumeIMP != superclassResumeIMP &&? ? ? ? ? ? ? ? originalAFResumeIMP != classResumeIMP) {//添加方法胶征、然后轉(zhuǎn)換方法的實現(xiàn)[selfswizzleResumeAndSuspendMethodForClass:currentClass];? ? ? ? ? ? }? ? ? ? ? ? currentClass = [currentClass superclass];? ? ? ? }? ? ? ? [localDataTask cancel];? ? ? ? [session finishTasksAndInvalidate];? ? }}/**
主要是實現(xiàn)了為一個類添加方法塞椎、并且轉(zhuǎn)換添加方法和原來對應(yīng)方法的實現(xiàn)桨仿。
@param theClass 要操作的類
*/+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {? ? Method afResumeMethod = class_getInstanceMethod(self,@selector(af_resume));? ? Method afSuspendMethod = class_getInstanceMethod(self,@selector(af_suspend));//為theClass類添加一個af_resume方法睛低。if(af_addMethod(theClass,@selector(af_resume), afResumeMethod)) {//把dataTask的resume和afresume方法的實現(xiàn)互換。af_swizzleSelector(theClass,@selector(resume),@selector(af_resume));? ? }//為theClass類添加一個af_suspend方法if(af_addMethod(theClass,@selector(af_suspend), afSuspendMethod)) {//把dataTask的suspend和af_suspend方法的實現(xiàn)互換服傍。af_swizzleSelector(theClass,@selector(suspend),@selector(af_suspend));? ? }}- (NSURLSessionTaskState)state {NSAssert(NO,@"State method should never be called in the actual dummy class");returnNSURLSessionTaskStateCanceling;}/**
在iOS7下面钱雷,`NSURLSessionDataTask`調(diào)用resume方法其實就是執(zhí)行`af_resume`的具體實現(xiàn)。
*/- (void)af_resume {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];//這里其實就是調(diào)用dataTask的resume實現(xiàn)[selfaf_resume];if(state !=NSURLSessionTaskStateRunning) {//這里的self其實就是`NSRULSessionDataTask`對象[[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];? ? }}/**
在iOS7下面吹零,`NSURLSessionDataTask`調(diào)用suspend方法其實就是執(zhí)行`af_suspend`的具體實現(xiàn)罩抗。
*/- (void)af_suspend {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];//這里其實就是調(diào)用dataTask的suspend具體實現(xiàn)[selfaf_suspend];if(state !=NSURLSessionTaskStateSuspended) {//這里的self其實就是`NSRULSessionDataTask`對象[[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];? ? }}@end
5 總結(jié)
AFURLSessionManager通過對task設(shè)置一個AFURLSessionManagerTaskDelegate代理來處理繁雜的請求進(jìn)度管理。從而降低了代碼的負(fù)責(zé)度灿椅。是代理模式的一個很好的實踐套蒂。
AFURLSessionManager通過私有類_AFURLSessionTaskSwizzling來修改iOS7和iOS8系統(tǒng)上面不同。是對于方法swizzle的一個成功和完整的實踐茫蛹。
AFURLSessionManager通過添加各種Block操刀,讓我們對請求過程有全方位的控制和處理。同時提供簡潔的api婴洼,把負(fù)責(zé)的處理全部封裝好骨坑。
作者:NS西北風(fēng)