AFNetworking初探

我們先看一下AFNetworking.h文件都給了我們什么方法

#import <Foundation/Foundation.h>



AFNetWorking基本上是所有iOS項(xiàng)目的標(biāo)配?,F(xiàn)在升級帶最新版的3.X了。得益于蘋果從NSURLConnection升級到NSURLSession,AFN也實(shí)現(xiàn)了api的簡化,同時(shí)功能卻一點(diǎn)沒少。我們來看一下AFN3.X的目錄結(jié)構(gòu):

AFNetWorking 這個(gè)文件是一個(gè)頭文件。啥也沒做,就是引入了其他文件方便使用。

AFURLSessionManager 這個(gè)文件是核心類,基本上通過它來實(shí)現(xiàn)了大部分核心功能。負(fù)責(zé)請求的建立、管理、銷毀、安全、請求重定向、請求重啟等各種功能。他主要實(shí)現(xiàn)了NSURLSession和NSRULSessionTask的封裝。

AFHTTPSessionManager 這個(gè)文件是AFURLSessionManager的子類。主要實(shí)現(xiàn)了對HTTP請求的優(yōu)化。

AFURLRequestSerialization 這個(gè)主要用于請求頭的編碼解碼、序列化、優(yōu)化處理、簡化請求拼接過程等。

AFURLResponseSerialization 這個(gè)主要用于網(wǎng)絡(luò)返回?cái)?shù)據(jù)的序列化、編碼解碼、序列化、數(shù)據(jù)處理等。

AFSecurityPolicy 這個(gè)主要用于請求的認(rèn)證功能。比如https的認(rèn)證模式等。

AFNetworkReachabilityManager 這個(gè)主要用于監(jiān)聽網(wǎng)絡(luò)請求狀態(tài)變化功能。

首先說明,看AFN源碼之前一定要搞清楚NSURLSession系列的api,這樣能讓你事半功倍,具體可以看AFNetWorking源碼之NSRULSession系列概述。在這篇文章里,我們主要講解AFURLSessionManager的實(shí)現(xiàn)原理和封裝過程。首先我們通過一個(gè)簡單的網(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è)置返回?cái)?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];}

通過這個(gè)請求,我們發(fā)現(xiàn)AFURLSessionManager要負(fù)責(zé)以下幾塊功能。

初始化和管理NSURLSession,通過它來建立和管理各種Task。

初始化和管理NSRULSessionTask,通過不同task來發(fā)送不同請求。

管理各種認(rèn)證功能、安全功能、請求重定向、數(shù)據(jù)處理。

管理和組織每個(gè)task的各種狀態(tài)管理和通知管理。不同task的回調(diào)處理。

幫我們管理和處理了NSRULSession系列api的各種代理方法。簡化了我們的處理。

2 AFURLSessionManager的聲明分析

AFURLSessionManager根據(jù)一個(gè)指定的NSURLSessionConfiguration創(chuàng)建和管理一個(gè)NSURLSession對象。并且這個(gè)對象實(shí)現(xiàn)了,,, 和這幾個(gè)協(xié)議的協(xié)議方法。同時(shí)實(shí)現(xiàn)NSSecureCoding和NSCopying來實(shí)現(xiàn)歸檔解檔和copy功能。

2.1AFURLSessionManager的初始化api

這些api主要用于初始化、安全策略、網(wǎng)絡(luò)狀態(tài)監(jiān)聽等:

interface AFURLSessionManager :NSObject//指定的初始化方法、通過他來初始化一個(gè)Manager對象。- (instancetype)initWithSessionConfiguration:(nullableNSURLSessionConfiguration*)configuration//AFURLSessionManager通過session來管理和創(chuàng)建網(wǎng)絡(luò)請求。一個(gè)manager就實(shí)現(xiàn)了對這個(gè)session的管理,他們是一一對應(yīng)的關(guān)系。@property(readonly,nonatomic,strong)NSURLSession*session;//處理網(wǎng)絡(luò)請求回調(diào)的操作隊(duì)列,就是我們初始化session的時(shí)候傳入的那個(gè)OperationQueue參數(shù)。如果不傳入,默認(rèn)是MainOperationQueue。@property(readonly,nonatomic,strong)NSOperationQueue*operationQueue;//對返回?cái)?shù)據(jù)的處理都通過這個(gè)屬性來處理,比如數(shù)據(jù)的提取、轉(zhuǎn)換等。默認(rèn)是一個(gè)`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ù)完成隊(duì)列處理、特殊情況的任務(wù)重新創(chuàng)建等:

//當(dāng)前session創(chuàng)建的所有Task,這個(gè)是下面三種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對象,如果不初始化、則一個(gè)默認(rèn)的Group被使用。@property(nonatomic,strong,nullable) dispatch_group_t completionGroup;//在iOS7的環(huán)境下,我們通過background模式的session創(chuàng)建的uploadTask有時(shí)會是nil,如果這個(gè)屬性是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)該說著幾個(gè)api就是AFN為我們提供的最大價(jià)值,他把所有delegate方法細(xì)節(jié)都處理好。直接提供給我們一些最實(shí)用的api,我們就不用去管理session系列繁瑣的delegate方法了。

//創(chuàng)建一個(gè)NSURLSessionDataTask- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//創(chuàng)建一個(gè)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)建一個(gè)上傳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)建一個(gè)上傳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)建一個(gè)uploadTask,然后上傳數(shù)據(jù)- (NSURLSessionUploadTask*)uploadTaskWithStreamedRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//新建一個(gè)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ù)一個(gè)download任務(wù)。resumeData參數(shù)表示的是恢復(fù)下載的時(shí)候初始化數(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更新,都必須手動(dòng)在主線程操作。

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出錯(cuò)或者無效的手的回調(diào)Block。這個(gè)Block主要在`NSURLSessionDelegate`代理的`URLSession:didBecomeInvalidWithError:`方法中執(zhí)行。- (void)setSessionDidBecomeInvalidBlock:(nullablevoid(^)(NSURLSession*session,NSError*error))block{? ? }//當(dāng)網(wǎng)絡(luò)請需要的認(rèn)證信息比如用戶名密碼已經(jīng)發(fā)送了的時(shí)候,就可以通過這個(gè)Block來處理。這個(gè)Block是在`NSURLSessionDelegate`代理里面的`URLSession:didReceiveChallenge:completionHandler:`方法中被執(zhí)行。注意這個(gè)是針對Session- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullableNSURLSessionAuthChallengeDisposition(^)(NSURLSession*session,NSURLAuthenticationChallenge*challenge,NSURLCredential* _Nullable __autoreleasing * _Nullable credential))block{? ? }////當(dāng)網(wǎng)絡(luò)請需要的認(rèn)證信息比如用戶名密碼已經(jīng)發(fā)送了的時(shí)候,就可以通過這個(gè)Block來處理。這個(gè)Block是在`NSURLSessionTaskDelegate`代理里面的`URLSession:task:didReceiveChallenge:completionHandler:`方法中被執(zhí)行。注意這個(gè)是針對Task。- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullableNSURLSessionAuthChallengeDisposition(^)(NSURLSession*session,NSURLSessionTask*task,NSURLAuthenticationChallenge*challenge,NSURLCredential* _Nullable __autoreleasing * _Nullable credential))block{? ? }//當(dāng)請求需要一個(gè)新的bodystream的時(shí)候,就可以通過這個(gè)Block來設(shè)置。這個(gè)Block在`NSURLSessionTaskDelegate` 代理協(xié)議的`URLSession:task:needNewBodyStream:`方法里面設(shè)置。- (void)setTaskNeedNewBodyStreamBlock:(nullableNSInputStream* (^)(NSURLSession*session,NSURLSessionTask*task))block{? ? }//當(dāng)一個(gè)網(wǎng)絡(luò)請求需要重定向的時(shí)候。就會調(diào)用這個(gè)Block。這個(gè)Block是在`NSURLSessionTaskDelegate`協(xié)議的`URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`方法中調(diào)用的。- (void)setTaskWillPerformHTTPRedirectionBlock:(nullableNSURLRequest* (^)(NSURLSession*session,NSURLSessionTask*task,NSURLResponse*response,NSURLRequest*request))block{? ? }//可以通過設(shè)置這個(gè)Block來獲取上傳進(jìn)度。這個(gè)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è)置一個(gè)Task完成以后執(zhí)行的Block,這個(gè)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)用這個(gè)Block。這個(gè)Block是在`NSURLSessionDataDelegate`協(xié)議的 `URLSession:dataTask:didReceiveResponse:completionHandler:`- (void)setDataTaskDidReceiveResponseBlock:(nullableNSURLSessionResponseDisposition(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSURLResponse*response))block{? ? }//如果一個(gè)dataTask轉(zhuǎn)換為downLoadTask以后,就可以設(shè)置這個(gè)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)用這個(gè)Block。具體在`NSURLSessionDataDelegate`協(xié)議的`URLSession:dataTask:didReceiveData:`方法。- (void)setDataTaskDidReceiveDataBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSData*data))block{? ? }//設(shè)置一個(gè)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ā)送出去以后,就可以通過這個(gè)Block來獲取。具體在`NSURLSessionDataDelegate`協(xié)議的 `URLSessionDidFinishEventsForBackgroundURLSession:`方法中。- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullablevoid(^)(NSURLSession*session))block{? ? }//當(dāng)一個(gè)downloadTask執(zhí)行完畢以后,可以通過這個(gè)Block來獲取下載信息,我們可以通過這個(gè)Block獲取下載文件的位置。具體在`NSURLSessionDownloadDelegate`協(xié)議的`URLSession:downloadTask:didFinishDownloadingToURL:`方法中被調(diào)用。- (void)setDownloadTaskDidFinishDownloadingBlock:(nullableNSURL* _Nullable? (^)(NSURLSession*session,NSURLSessionDownloadTask*downloadTask,NSURL*location))block{? ? }//可以通過這個(gè)Block獲取一個(gè)downloadTask的下載進(jìn)度。這個(gè)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)一個(gè)downloadTask重新開始以后,我們可以通過這個(gè)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的實(shí)現(xiàn)分析

AFURLSessionManager.m文件里面除了有AFURLSessionManager.h定義的各種接口的實(shí)現(xiàn)意外,還有處理不同iOS版本下NSRULSession不同的部分,以及多個(gè)全局dispatch_queue_t的定義、以及處理NSURLSeesionTash的各種代理方法的實(shí)現(xiàn)和處理。具體劃分如下:

NSURLSessionManager的實(shí)現(xiàn)。主要實(shí)現(xiàn)了接口文件定義的各種api的實(shí)現(xiàn),比如Task的創(chuàng)建、Task的獲取、Task的各種代理方法的實(shí)現(xiàn)、NSCoping和NSCoding協(xié)議、以及各種Block的實(shí)現(xiàn)。

基本屬性的初始化。比如sessionConfiguration、operationQueue、session、mutableTaskDelegatesKeyedByTaskIdentifier等屬性。以及用于實(shí)現(xiàn)task和AFURLSessionManagerTaskDelegate的綁定的taskDescriptionForSessionTasks、還有關(guān)鍵操作的鎖屬性lock。

接口文件的各種Block對應(yīng)的屬性,一個(gè)Block對應(yīng)一個(gè)屬性。

處理Task暫停與重啟操作的方法。

給Task設(shè)置AFURLSessionManagerTaskDelegate代理的方法。

初始化Task的各種方法。

設(shè)置B接口文件定義的各種Block。

NSURLSession系列代理方法。

_AFURLSessionTaskSwizzling私有類。主要實(shí)現(xiàn)了iOS7和iOS8系統(tǒng)上NSURLSession差別的處理。讓不同系統(tǒng)版本NSURLSession版本基本一致。

AFURLSessionManagerTaskDelegate這個(gè)類主要是把NSURLSeesion的部分代理方法讓他處理。從而達(dá)到簡化代碼的目的。

處理Task的上傳或者下載進(jìn)度。

處理封裝NSURLSeesion返回的數(shù)據(jù)。

Task完成等的通知封裝。

全局dispatch_queue_t和dispatch_group_t的定義。各種通知名稱的初始化,各種Block的類型定義。

3.1 AFURLSessionManager一個(gè)網(wǎng)絡(luò)請求實(shí)現(xiàn)過程

我們通過一個(gè)網(wǎng)絡(luò)請求過程來分析AFURLSessionManager.m的實(shí)現(xiàn)。我們通過initWithSessionConfiguration方法初始化一個(gè)manager。在這個(gè)方法里會初始化各種屬性、以及為session屬性設(shè)置代理:

接口文件中的代碼如下:

AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]];

實(shí)現(xiàn)文件中對應(yīng)的處理如下:

/**

初始化方法

@return 返回一個(gè)manager對象

*/- (instancetype)init {return[selfinitWithSessionConfiguration:nil];}/**

默認(rèn)初始化方法、通過這個(gè)方法來做manager的具體化初始化動(dòng)作

@param configuration NSURLSession的配置

@return 返回一個(gè)manager對象

*/- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration*)configuration {self= [superinit];if(!self) {returnnil;? ? }//如果用戶沒有手動(dòng)指定,則使用默認(rèn)的configuration來初始化if(!configuration) {? ? ? ? configuration = [NSURLSessionConfigurationdefaultSessionConfiguration];? ? }//賦值給屬性self.sessionConfiguration = configuration;//初始化NSURLSession的task代理方法執(zhí)行的隊(duì)列。//這里有一個(gè)很關(guān)鍵的點(diǎn)是task的代理執(zhí)行的queque一次性只能執(zhí)行一個(gè)task。這樣就避免了task的代理方法執(zhí)行的混亂。self.operationQueue = [[NSOperationQueuealloc] init];self.operationQueue.maxConcurrentOperationCount =1;//出絲滑NSURLSession對象,最核心的對象。self.session = [NSURLSessionsessionWithConfiguration:self.sessionConfiguration delegate:selfdelegateQueue:self.operationQueue];//如果用戶沒有手動(dòng)指定,則返回的數(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)系。通過這個(gè)self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionaryalloc] init];//初始化一個(gè)鎖對象,關(guān)鍵操作加鎖。self.lock = [[NSLockalloc] init];self.lock.name = AFURLSessionManagerLockName;/**

? ? 獲取當(dāng)前session正在執(zhí)行的所有Task。同時(shí)為每一個(gè)Task添加`AFURLSessionManagerTaskDelegate`代理對象,這個(gè)代理對象主要用于管理uplaodTak和downloadTask的進(jìn)度管理。并且在Task執(zhí)行完畢以后調(diào)用相應(yīng)的Block。同時(shí)發(fā)送相應(yīng)的notification對象,實(shí)現(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);}];

實(shí)現(xiàn)文件則調(diào)用了很多方法:

1 首先是初始化一個(gè)NSURLSessionDownLoadTask對象

//通過session創(chuàng)建一個(gè)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è)置一個(gè)AFURLSessionManagerTaskDelegate代理對象。從而可以實(shí)現(xiàn)對進(jìn)度處理、Block調(diào)用、Task完成返回?cái)?shù)據(jù)的拼裝的功能。

//根據(jù)指定的Task,初始化一個(gè)AFURLSessionManagerTaskDelegateAFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];? ? delegate.manager =self;//設(shè)置Task完成的回調(diào)Blockdelegate.completionHandler = completionHandler;if(destination) {//任務(wù)完成以后,調(diào)用destination這個(gè)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è)置一個(gè)下載進(jìn)度的Block,以便在后面代理方法中調(diào)用。delegate.downloadProgressBlock = downloadProgressBlock;

3 初始化一個(gè)AFURLSessionManagerTaskDelegate對象。在這個(gè)對象中對Task的請求過程進(jìn)行處理和控制。

/**

初始化一個(gè)AFURLSessionManagerTaskDelegate對象

@param task 對象綁定的Task

@return 返回對象

*/- (instancetype)initWithTask:(NSURLSessionTask*)task {self= [superinit];if(!self) {returnnil;? ? }//這個(gè)屬性用于存儲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對象取消的時(shí)候,取消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)系。通過一個(gè)字典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í)行。其實(shí)就是[downloadTask resume];執(zhí)行以后開始。

/**

在網(wǎng)絡(luò)請求正式開始以后,這個(gè)方法會在數(shù)據(jù)接收的過程中多次調(diào)用。我們可以通過這個(gè)方法獲取數(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`類中的代理方法。從而實(shí)現(xiàn)對于進(jìn)度更新等功能。//會調(diào)用下面的那個(gè)方法[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];? ? }if(self.downloadTaskDidWriteData) {//如果有`downloadTaskDidWriteData`Block的實(shí)現(xiàn),則在這個(gè)調(diào)用Block從而實(shí)現(xiàn)對下載進(jìn)度過程的控制。self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);? ? }}//AFURLSessionManagerTaskDelegate里面的這個(gè)代理方法實(shí)現(xiàn)對進(jìn)度的更新。- (void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask? ? ? didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{//AFURLSessionManagerTaskDelegate代理方法實(shí)現(xiàn)對下載進(jìn)度的記錄self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;self.downloadProgress.completedUnitCount = totalBytesWritten;}

6 Task完成以后,會調(diào)用AFURLSessionManagerTaskDelegate對象的方法對返回的數(shù)據(jù)封裝。

//AFURLSessionManagerTaskDelegate里面的這個(gè)代理方法實(shí)現(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;? ? }//有錯(cuò)封裝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)用這個(gè)Blockif(self.completionHandler) {self.completionHandler(task.response, responseObject, error);? ? ? ? ? ? }//發(fā)送一個(gè)指定Task結(jié)束的通知dispatch_async(dispatch_get_main_queue(), ^{? ? ? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];? ? ? ? ? ? });? ? ? ? });? ? }else{//正確數(shù)據(jù)封裝//在一個(gè)并行的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)用這個(gè)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對象的代理方法里面手動(dòng)調(diào)用AFURLSessionManagerTaskDelegate對應(yīng)的代理方法來實(shí)現(xiàn)對數(shù)據(jù)的處理和簡化代碼的作用,這個(gè)設(shè)計(jì)思路的確吊吊的。還有一些方法沒有涉及到,不過大同小異,基本過程就是這樣,就不一一解釋了。

3.2 AFURLSessionManager一些特殊模塊的說明

AFURLSeeesionManager實(shí)現(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)對象歸檔的時(shí)候,只歸檔了`NSURLSessionConfiguration`屬性。所以說歸檔接檔的時(shí)候所有Block設(shè)置、operation設(shè)置都會失效。

@param coder coder

*/- (void)encodeWithCoder:(NSCoder*)coder {? ? [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];}

同時(shí),AFURLSessionManager也實(shí)現(xiàn)了NSCopying協(xié)議。通過協(xié)議的實(shí)現(xiàn)過程,我們發(fā)現(xiàn)也是只使用了NSURLSessionConfiguration屬性。和歸檔解檔一樣。

#pragma mark - 實(shí)現(xiàn)NSCopying協(xié)議。copy的NAURLSessionManager沒有復(fù)制任何與代理處理相關(guān)的Block- (instancetype)copyWithZone:(NSZone*)zone {return[[[selfclass] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];}

有的時(shí)候,我們的請求會返回302這個(gè)狀態(tài)碼,這個(gè)表示需要請求重定向到另一個(gè)url,我們可以下面這個(gè)代理方法里面決定對于重定向的處理,如果對completionHandler傳入nil,則會把response傳入重定向請求。另外,backgroundSession的Task不會調(diào)用下面這個(gè)代理方法,而是直接調(diào)用。

/**

有的時(shí)候,我們的請求會返回302這個(gè)狀態(tài)碼,這個(gè)表示需要請求重定向到另一個(gè)url,我們可以在這個(gè)代理方法里面絕定對于重定向的處理。

@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這個(gè)Block,我們就通過這個(gè)Block的調(diào)用返回處理完成的request對象。if(self.taskWillPerformHTTPRedirection) {? ? ? ? redirectRequest =self.taskWillPerformHTTPRedirection(session, task, response, request);? ? }//這個(gè)調(diào)用是必須的,執(zhí)行重定向操作。if(completionHandler) {? ? ? ? completionHandler(redirectRequest);? ? }}

創(chuàng)建NSRULSessionUplaodTask的時(shí)候,在某些系統(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)建一個(gè)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ā)送一個(gè)信號量,讓semaphore變?yōu)?。此時(shí)表示tasks已經(jīng)成功獲取。dispatch_semaphore_signal(semaphore);? ? }];//這里會一直等待信號量變?yōu)?。dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//返回Task。通過信號量控制,避免了方法結(jié)束的時(shí)候,tasks還沒有正常獲取的情況。returntasks;}

4 _AFURLSessionTaskSwizzling私有類的說明

在iOS7和iOS8及以上的系統(tǒng),NSRULSessionTask的具體實(shí)現(xiàn)是不同的。我們目前知道的不同有:

NSURLSessionTasks是一個(gè)類簇。所以我們初始化一個(gè)Task的時(shí)候,我們并不只到初始化的到底是哪個(gè)子類。

簡單的通過[NSURLSessionTask class]并不會起作用。必須通過NSURLSession創(chuàng)建一個(gè)task對象。然后獲取他所在的類。

iOS7下面,下面代碼中的localDataTask對象的繼承關(guān)系是__NSCFLocalDataTask->__NSCFLocalSessionTask->__NSCFURLSessionTask。

在iOS8以及以上系統(tǒng)。下面代碼中的localDataTask對象的繼承關(guān)系是__NSCFLocalDataTask->__NSCFLocalSessionTask->NSURLSessionTask。

在iOS7下面__NSCFLocalSessionTask和__NSCFURLSessionTask實(shí)現(xiàn)了resume和suspend方法,同時(shí)最重要的是他不調(diào)用父類的實(shí)現(xiàn)。但是iOS8下面,只有NSURLSessionTask實(shí)現(xiàn)了resume和suspend。所以在iOS7的環(huán)境下,我們需要想辦法讓resume和suspend調(diào)用NSURLSessionTask的具體實(shí)現(xiàn)。

下面的代碼完美的向我們展示了一個(gè)向類添加方法,并且swizzle方法實(shí)現(xiàn)的過程。值得仔細(xì)琢磨。

/**

切換theClass類的`originalSelector`和`swizzledSelector`的實(shí)現(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);}/**

動(dòng)態(tài)給一個(gè)類添加方法

@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://初始化一個(gè)dataTask對象NSURLSessionDataTask*localDataTask = [session dataTaskWithURL:nil];#pragma clang diagnostic pop//獲取af_resume這個(gè)方法的實(shí)現(xiàn)。IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([selfclass],@selector(af_resume)));//獲取dataTask的具體類Class currentClass = [localDataTaskclass];//如果父類有resume方法。則改變方法的具體實(shí)現(xiàn)。while(class_getInstanceMethod(currentClass,@selector(resume))) {? ? ? ? ? ? Class superClass = [currentClass superclass];//找到類和父類的resume方法實(shí)現(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)換方法的實(shí)現(xiàn)[selfswizzleResumeAndSuspendMethodForClass:currentClass];? ? ? ? ? ? }? ? ? ? ? ? currentClass = [currentClass superclass];? ? ? ? }? ? ? ? [localDataTask cancel];? ? ? ? [session finishTasksAndInvalidate];? ? }}/**

主要是實(shí)現(xiàn)了為一個(gè)類添加方法、并且轉(zhuǎn)換添加方法和原來對應(yīng)方法的實(shí)現(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類添加一個(gè)af_resume方法。if(af_addMethod(theClass,@selector(af_resume), afResumeMethod)) {//把dataTask的resume和afresume方法的實(shí)現(xiàn)互換。af_swizzleSelector(theClass,@selector(resume),@selector(af_resume));? ? }//為theClass類添加一個(gè)af_suspend方法if(af_addMethod(theClass,@selector(af_suspend), afSuspendMethod)) {//把dataTask的suspend和af_suspend方法的實(shí)現(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方法其實(shí)就是執(zhí)行`af_resume`的具體實(shí)現(xiàn)。

*/- (void)af_resume {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];//這里其實(shí)就是調(diào)用dataTask的resume實(shí)現(xiàn)[selfaf_resume];if(state !=NSURLSessionTaskStateRunning) {//這里的self其實(shí)就是`NSRULSessionDataTask`對象[[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];? ? }}/**

在iOS7下面,`NSURLSessionDataTask`調(diào)用suspend方法其實(shí)就是執(zhí)行`af_suspend`的具體實(shí)現(xiàn)。

*/- (void)af_suspend {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];//這里其實(shí)就是調(diào)用dataTask的suspend具體實(shí)現(xiàn)[selfaf_suspend];if(state !=NSURLSessionTaskStateSuspended) {//這里的self其實(shí)就是`NSRULSessionDataTask`對象[[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];? ? }}@end

5 總結(jié)

AFURLSessionManager通過對task設(shè)置一個(gè)AFURLSessionManagerTaskDelegate代理來處理繁雜的請求進(jìn)度管理。從而降低了代碼的負(fù)責(zé)度。是代理模式的一個(gè)很好的實(shí)踐。

AFURLSessionManager通過私有類_AFURLSessionTaskSwizzling來修改iOS7和iOS8系統(tǒng)上面不同。是對于方法swizzle的一個(gè)成功和完整的實(shí)踐。

AFURLSessionManager通過添加各種Block,讓我們對請求過程有全方位的控制和處理。同時(shí)提供簡潔的api,把負(fù)責(zé)的處理全部封裝好。

作者:NS西北風(fēng)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,579評論 30 472
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,192評論 8 265
  • iOS開發(fā)系列--網(wǎng)絡(luò)開發(fā) 概覽 大部分應(yīng)用程序都或多或少會牽扯到網(wǎng)絡(luò)開發(fā),例如說新浪微博、微信等,這些應(yīng)用本身可...
    lichengjin閱讀 4,022評論 2 7
  • 今天早上就想到要寫寫生活的常態(tài)和什么是生活中真正的歡愉,這兩個(gè)問題其實(shí)是有很大的關(guān)聯(lián)性的,下午發(fā)生的事情又讓我想...
    牧羊少年_奇閱讀 338評論 0 1
  • 當(dāng)太陽升起 當(dāng)冬天過去 當(dāng)孩子早起 當(dāng)年老無依 是否懷念過去 是否想起曾經(jīng)
    今朝3閱讀 219評論 0 0

友情鏈接更多精彩內(nèi)容