我們先看一下AFNetworking.h文件都給了我們什么方法
#import <Foundation/Foundation.h>
AFNetWorking基本上是所有iOS項目的標配。現在升級帶最新版的3.X了。得益于蘋果從NSURLConnection升級到NSURLSession,AFN也實現了api的簡化,同時功能卻一點沒少。我們來看一下AFN3.X的目錄結構:
AFNetWorking 這個文件是一個頭文件。啥也沒做,就是引入了其他文件方便使用。
AFURLSessionManager 這個文件是核心類,基本上通過它來實現了大部分核心功能。負責請求的建立、管理、銷毀、安全、請求重定向、請求重啟等各種功能。他主要實現了NSURLSession和NSRULSessionTask的封裝。
AFHTTPSessionManager 這個文件是AFURLSessionManager的子類。主要實現了對HTTP請求的優化。
AFURLRequestSerialization 這個主要用于請求頭的編碼解碼、序列化、優化處理、簡化請求拼接過程等。
AFURLResponseSerialization 這個主要用于網絡返回數據的序列化、編碼解碼、序列化、數據處理等。
AFSecurityPolicy 這個主要用于請求的認證功能。比如https的認證模式等。
AFNetworkReachabilityManager 這個主要用于監聽網絡請求狀態變化功能。
首先說明,看AFN源碼之前一定要搞清楚NSURLSession系列的api,這樣能讓你事半功倍,具體可以看AFNetWorking源碼之NSRULSession系列概述。在這篇文章里,我們主要講解AFURLSessionManager的實現原理和封裝過程。首先我們通過一個簡單的網絡請求看一下他的基本用法(大部分都是非必須的,這里為了掩飾寫出來):
- (IBAction)clickButton:(id)sender {//通過默認配置初始化SessionNSURLSessionConfiguration*configuration = [NSURLSessionConfigurationdefaultSessionConfiguration];? ? AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];//設置網絡請求序列化對象AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer];? ? [requestSerializer setValue:@"test"forHTTPHeaderField:@"requestHeader"];? ? requestSerializer.timeoutInterval =60;? ? requestSerializer.stringEncoding =NSUTF8StringEncoding;//設置返回數據序列化對象AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];? ? manager.responseSerializer = responseSerializer;//網絡請求安全策略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;? ? ? ? }];? ? }//監聽網絡狀態[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(@"下載進度:%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];}
通過這個請求,我們發現AFURLSessionManager要負責以下幾塊功能。
初始化和管理NSURLSession,通過它來建立和管理各種Task。
初始化和管理NSRULSessionTask,通過不同task來發送不同請求。
管理各種認證功能、安全功能、請求重定向、數據處理。
管理和組織每個task的各種狀態管理和通知管理。不同task的回調處理。
幫我們管理和處理了NSRULSession系列api的各種代理方法。簡化了我們的處理。
2 AFURLSessionManager的聲明分析
AFURLSessionManager根據一個指定的NSURLSessionConfiguration創建和管理一個NSURLSession對象。并且這個對象實現了,,, 和這幾個協議的協議方法。同時實現NSSecureCoding和NSCopying來實現歸檔解檔和copy功能。
2.1AFURLSessionManager的初始化api
這些api主要用于初始化、安全策略、網絡狀態監聽等:
interface AFURLSessionManager :NSObject//指定的初始化方法、通過他來初始化一個Manager對象。- (instancetype)initWithSessionConfiguration:(nullableNSURLSessionConfiguration*)configuration//AFURLSessionManager通過session來管理和創建網絡請求。一個manager就實現了對這個session的管理,他們是一一對應的關系。@property(readonly,nonatomic,strong)NSURLSession*session;//處理網絡請求回調的操作隊列,就是我們初始化session的時候傳入的那個OperationQueue參數。如果不傳入,默認是MainOperationQueue。@property(readonly,nonatomic,strong)NSOperationQueue*operationQueue;//對返回數據的處理都通過這個屬性來處理,比如數據的提取、轉換等。默認是一個`AFJSONResponseSerializer`對象用JSON的方式解析。@property(nonatomic,strong)id responseSerializer;//用于指定session的安全策略。用于處理信任主機和證書認證等。默認是`defaultPolicy`。@property(nonatomic,strong) AFSecurityPolicy *securityPolicy;//觀測網絡狀態的變化,具體可以看我的Demo用法。@property(readwrite,nonatomic,strong) AFNetworkReachabilityManager *reachabilityManager;@end
2.2AFURLSessionManager獲取Task的api
這部分api主要是任務的創建、任務的分類、任務完成隊列處理、特殊情況的任務重新創建等:
//當前session創建的所有Task,這個是下面三種task的總和。@property(readonly,nonatomic,strong)NSArray *tasks;//當前session創建的DataTask@property(readonly,nonatomic,strong)NSArray *dataTasks;//當前session創建的uploadTask@property(readonly,nonatomic,strong)NSArray *uploadTasks;//當前session創建的downloadTask@property(readonly,nonatomic,strong)NSArray *downloadTasks;//用于處理任務回調的GCD對象,默認是dispatch_main_queue。@property(nonatomic,strong,nullable)dispatch_queue_tcompletionQueue;//用于處理任務回調的GCD的group對象,如果不初始化、則一個默認的Group被使用。@property(nonatomic,strong,nullable) dispatch_group_t completionGroup;//在iOS7的環境下,我們通過background模式的session創建的uploadTask有時會是nil,如果這個屬性是yes,AFN會嘗試再次創建uploadTask。@property(nonatomic,assign)BOOLattemptsToRecreateUploadTasksForBackgroundSessions;//廢除manager對應的Session。通過傳入的參數來決定是否立即取消已經用session發出去的任務。- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
2.3AFURLSessionManager為管理Task創建Block
AFURLSessionManager提供了很多創建Task的api。并且提供了很多處理Task的Block。應該說著幾個api就是AFN為我們提供的最大價值,他把所有delegate方法細節都處理好。直接提供給我們一些最實用的api,我們就不用去管理session系列繁瑣的delegate方法了。
//創建一個NSURLSessionDataTask- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//創建一個NSURLSessionDataTask,并且能獲取上傳或者下載進度- (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;//創建一個上傳Task,并且指定上傳文件的路徑。- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fromFile:(NSURL*)fileURL? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;////創建一個上傳Task,并且指定上傳的數據。- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fromData:(nullableNSData*)bodyData? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//創建一個uploadTask,然后上傳數據- (NSURLSessionUploadTask*)uploadTaskWithStreamedRequest:(NSURLRequest*)request? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;//新建一個download任務,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;//繼續恢復一個download任務。resumeData參數表示的是恢復下載的時候初始化數據,比如前面已經下載好的部分數據。- (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的上傳進度- (nullableNSProgress*)uploadProgressForTask:(NSURLSessionTask*)task;//獲取指定Task的下載進度- (nullableNSProgress*)downloadProgressForTask:(NSURLSessionTask*)task;
注意:上面所有Task的progress都不在主線程、所以要在progress中做UI更新,都必須手動在主線程操作。
2.4AFURLSessionManager設置各種情況的代理回調
這些回調Block主要是用于處理網絡請求過程或者結束以后的數據處理、認證、通知、緩存等。我們可以通過設置這些Block來獲取或者檢測各種狀態。相當于就是鉤子函數。通過下面的這些Block,我們基本可以獲取請求過程中的所有狀態以及需要做的各種處理。
//設置Session出錯或者無效的手的回調Block。這個Block主要在`NSURLSessionDelegate`代理的`URLSession:didBecomeInvalidWithError:`方法中執行。- (void)setSessionDidBecomeInvalidBlock:(nullablevoid(^)(NSURLSession*session,NSError*error))block{? ? }//當網絡請需要的認證信息比如用戶名密碼已經發送了的時候,就可以通過這個Block來處理。這個Block是在`NSURLSessionDelegate`代理里面的`URLSession:didReceiveChallenge:completionHandler:`方法中被執行。注意這個是針對Session- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullableNSURLSessionAuthChallengeDisposition(^)(NSURLSession*session,NSURLAuthenticationChallenge*challenge,NSURLCredential* _Nullable __autoreleasing * _Nullable credential))block{? ? }////當網絡請需要的認證信息比如用戶名密碼已經發送了的時候,就可以通過這個Block來處理。這個Block是在`NSURLSessionTaskDelegate`代理里面的`URLSession:task:didReceiveChallenge:completionHandler:`方法中被執行。注意這個是針對Task。- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullableNSURLSessionAuthChallengeDisposition(^)(NSURLSession*session,NSURLSessionTask*task,NSURLAuthenticationChallenge*challenge,NSURLCredential* _Nullable __autoreleasing * _Nullable credential))block{? ? }//當請求需要一個新的bodystream的時候,就可以通過這個Block來設置。這個Block在`NSURLSessionTaskDelegate` 代理協議的`URLSession:task:needNewBodyStream:`方法里面設置。- (void)setTaskNeedNewBodyStreamBlock:(nullableNSInputStream* (^)(NSURLSession*session,NSURLSessionTask*task))block{? ? }//當一個網絡請求需要重定向的時候。就會調用這個Block。這個Block是在`NSURLSessionTaskDelegate`協議的`URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`方法中調用的。- (void)setTaskWillPerformHTTPRedirectionBlock:(nullableNSURLRequest* (^)(NSURLSession*session,NSURLSessionTask*task,NSURLResponse*response,NSURLRequest*request))block{? ? }//可以通過設置這個Block來獲取上傳進度。這個Block主要在`NSURLSessionTaskDelegate`協議的 `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`方法中調用.- (void)setTaskDidSendBodyDataBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionTask*task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block{? ? }//設置一個Task完成以后執行的Block,這個Block在`NSURLSessionTaskDelegate`協議的 `URLSession:task:didCompleteWithError:`方法中執行。- (void)setTaskDidCompleteBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionTask*task,NSError* _Nullable error))block{? ? }//當接收到網絡請求返回以后,可以調用這個Block。這個Block是在`NSURLSessionDataDelegate`協議的 `URLSession:dataTask:didReceiveResponse:completionHandler:`- (void)setDataTaskDidReceiveResponseBlock:(nullableNSURLSessionResponseDisposition(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSURLResponse*response))block{? ? }//如果一個dataTask轉換為downLoadTask以后,就可以設置這個Block來調用。在`NSURLSessionDataDelegate` 協議的`URLSession:dataTask:didBecomeDownloadTask:`方法中調用。- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSURLSessionDownloadTask*downloadTask))block{? ? }//當dataTask接收到數據以后,可以設置調用這個Block。具體在`NSURLSessionDataDelegate`協議的`URLSession:dataTask:didReceiveData:`方法。- (void)setDataTaskDidReceiveDataBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSData*data))block{? ? }//設置一個Block來決定是否處理或者換成網絡請求緩存。具體在`NSURLSessionDataDelegate`協議的`URLSession:dataTask:willCacheResponse:completionHandler:`方法中。- (void)setDataTaskWillCacheResponseBlock:(nullableNSCachedURLResponse* (^)(NSURLSession*session,NSURLSessionDataTask*dataTask,NSCachedURLResponse*proposedResponse))block{? ? }//當session所有的任務都發送出去以后,就可以通過這個Block來獲取。具體在`NSURLSessionDataDelegate`協議的 `URLSessionDidFinishEventsForBackgroundURLSession:`方法中。- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullablevoid(^)(NSURLSession*session))block{? ? }//當一個downloadTask執行完畢以后,可以通過這個Block來獲取下載信息,我們可以通過這個Block獲取下載文件的位置。具體在`NSURLSessionDownloadDelegate`協議的`URLSession:downloadTask:didFinishDownloadingToURL:`方法中被調用。- (void)setDownloadTaskDidFinishDownloadingBlock:(nullableNSURL* _Nullable? (^)(NSURLSession*session,NSURLSessionDownloadTask*downloadTask,NSURL*location))block{? ? }//可以通過這個Block獲取一個downloadTask的下載進度。這個Block會在下載過程中多次被調用。具體是在`NSURLSessionDownloadDelegate`協議中的`URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`方法中被調用。- (void)setDownloadTaskDidWriteDataBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDownloadTask*downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block{? ? }//當一個downloadTask重新開始以后,我們可以通過這個Block獲取fileOffSet等信息獲取已經下載的部分以及總共有多少要下載。具體是在`NSURLSessionDownloadDelegate`協議的`URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`方法中被調用。- (void)setDownloadTaskDidResumeBlock:(nullablevoid(^)(NSURLSession*session,NSURLSessionDownloadTask*downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block{? ? }
除了上面的部分,AFURLSessionManager的頭文件還提供了很多notification的聲明。通過這些通知,我們可以獲取Task是否開始、是否完成、是否掛起、是否無效等各種通知。具體可以去文件里看。
3 AFURLSessionManager的實現分析
AFURLSessionManager.m文件里面除了有AFURLSessionManager.h定義的各種接口的實現意外,還有處理不同iOS版本下NSRULSession不同的部分,以及多個全局dispatch_queue_t的定義、以及處理NSURLSeesionTash的各種代理方法的實現和處理。具體劃分如下:
NSURLSessionManager的實現。主要實現了接口文件定義的各種api的實現,比如Task的創建、Task的獲取、Task的各種代理方法的實現、NSCoping和NSCoding協議、以及各種Block的實現。
基本屬性的初始化。比如sessionConfiguration、operationQueue、session、mutableTaskDelegatesKeyedByTaskIdentifier等屬性。以及用于實現task和AFURLSessionManagerTaskDelegate的綁定的taskDescriptionForSessionTasks、還有關鍵操作的鎖屬性lock。
接口文件的各種Block對應的屬性,一個Block對應一個屬性。
處理Task暫停與重啟操作的方法。
給Task設置AFURLSessionManagerTaskDelegate代理的方法。
初始化Task的各種方法。
設置B接口文件定義的各種Block。
NSURLSession系列代理方法。
_AFURLSessionTaskSwizzling私有類。主要實現了iOS7和iOS8系統上NSURLSession差別的處理。讓不同系統版本NSURLSession版本基本一致。
AFURLSessionManagerTaskDelegate這個類主要是把NSURLSeesion的部分代理方法讓他處理。從而達到簡化代碼的目的。
處理Task的上傳或者下載進度。
處理封裝NSURLSeesion返回的數據。
Task完成等的通知封裝。
全局dispatch_queue_t和dispatch_group_t的定義。各種通知名稱的初始化,各種Block的類型定義。
3.1 AFURLSessionManager一個網絡請求實現過程
我們通過一個網絡請求過程來分析AFURLSessionManager.m的實現。我們通過initWithSessionConfiguration方法初始化一個manager。在這個方法里會初始化各種屬性、以及為session屬性設置代理:
接口文件中的代碼如下:
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration]];
實現文件中對應的處理如下:
/**
初始化方法
@return 返回一個manager對象
*/- (instancetype)init {return[selfinitWithSessionConfiguration:nil];}/**
默認初始化方法、通過這個方法來做manager的具體化初始化動作
@param configuration NSURLSession的配置
@return 返回一個manager對象
*/- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration*)configuration {self= [superinit];if(!self) {returnnil;? ? }//如果用戶沒有手動指定,則使用默認的configuration來初始化if(!configuration) {? ? ? ? configuration = [NSURLSessionConfigurationdefaultSessionConfiguration];? ? }//賦值給屬性self.sessionConfiguration = configuration;//初始化NSURLSession的task代理方法執行的隊列。//這里有一個很關鍵的點是task的代理執行的queque一次性只能執行一個task。這樣就避免了task的代理方法執行的混亂。self.operationQueue = [[NSOperationQueuealloc] init];self.operationQueue.maxConcurrentOperationCount =1;//出絲滑NSURLSession對象,最核心的對象。self.session = [NSURLSessionsessionWithConfiguration:self.sessionConfiguration delegate:selfdelegateQueue:self.operationQueue];//如果用戶沒有手動指定,則返回的數據是JSON格式序列化。self.responseSerializer = [AFJSONResponseSerializer serializer];//指定https處理的安全策略。self.securityPolicy = [AFSecurityPolicy defaultPolicy];#if !TARGET_OS_WATCH//初始化網絡狀態監聽屬性self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];#endif//用于記錄Task與他的`AFURLSessionManagerTaskDelegate`代理對象的一一對應關系。通過這個self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionaryalloc] init];//初始化一個鎖對象,關鍵操作加鎖。self.lock = [[NSLockalloc] init];self.lock.name = AFURLSessionManagerLockName;/**
? ? 獲取當前session正在執行的所有Task。同時為每一個Task添加`AFURLSessionManagerTaskDelegate`代理對象,這個代理對象主要用于管理uplaodTak和downloadTask的進度管理。并且在Task執行完畢以后調用相應的Block。同時發送相應的notification對象,實現對task數據或者狀態改變的檢測。
? ? @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;}
請求執行,接口文件如下:
NSURLSessionDownloadTask*downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress*downloadProgress){NSLog(@"下載進度:%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);}];
實現文件則調用了很多方法:
1 首先是初始化一個NSURLSessionDownLoadTask對象
//通過session創建一個downloadTask,__blockNSURLSessionDownloadTask*downloadTask =nil;//url_session_manager_create_task_safely作用是修復在iOS8下面的系統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設置一個AFURLSessionManagerTaskDelegate代理對象。從而可以實現對進度處理、Block調用、Task完成返回數據的拼裝的功能。
//根據指定的Task,初始化一個AFURLSessionManagerTaskDelegateAFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];? ? delegate.manager =self;//設置Task完成的回調Blockdelegate.completionHandler = completionHandler;if(destination) {//任務完成以后,調用destination這個Blockdelegate.downloadTaskDidFinishDownloading = ^NSURL* (NSURLSession* __unused session,NSURLSessionDownloadTask*task,NSURL*location) {returndestination(location, task.response);? ? ? ? };? ? }//指定Task與taskDescriptionForSessionTasks的關聯關系,方便后面的通知中做對應的處理。downloadTask.taskDescription =self.taskDescriptionForSessionTasks;//添加通知[selfsetDelegate:delegate forTask:downloadTask];//設置一個下載進度的Block,以便在后面代理方法中調用。delegate.downloadProgressBlock = downloadProgressBlock;
3 初始化一個AFURLSessionManagerTaskDelegate對象。在這個對象中對Task的請求過程進行處理和控制。
/**
初始化一個AFURLSessionManagerTaskDelegate對象
@param task 對象綁定的Task
@return 返回對象
*/- (instancetype)initWithTask:(NSURLSessionTask*)task {self= [superinit];if(!self) {returnnil;? ? }//這個屬性用于存儲Task下載過程中的數據_mutableData = [NSMutableDatadata];//存儲Task上傳和下載的進度_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;//當progress對象取消的時候,取消Taskprogress.cancellationHandler = ^{? ? ? ? ? ? [weakTask cancel];? ? ? ? };? ? ? ? progress.pausable =YES;? ? ? ? progress.pausingHandler = ^{//掛起Task[weakTask suspend];? ? ? ? };if([progress respondsToSelector:@selector(setResumingHandler:)]) {? ? ? ? ? ? progress.resumingHandler = ^{//重啟Task[weakTask resume];? ? ? ? ? ? };? ? ? ? }//更具progress的進度來獲取Task的進度。fractionCompleted方法在請求過程中多次執行。[progress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))? ? ? ? ? ? ? ? ? ? ? options:NSKeyValueObservingOptionNewcontext:NULL];? ? }returnself;}//上面通過對fractionCompleted方法KVO。則會調用下面的方法,從而執行manager的- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context {if([object isEqual:self.downloadProgress]) {//更新下載進度Blockif(self.downloadProgressBlock) {self.downloadProgressBlock(object);? ? ? ? }? ? }elseif([object isEqual:self.uploadProgress]) {//更新上傳進度Blocif(self.uploadProgressBlock) {self.uploadProgressBlock(object);? ? ? ? }? ? }}
4 在AFURLSessionManagerTaskDelegate設置Task狀態改變的監聽。
/**
設置指定task的`AFURLSessionManagerTaskDelegate`對象。并且添加task掛起或者重啟的監聽。
@param delegate 代理對象
@param task task
*/- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate? ? ? ? ? ? forTask:(NSURLSessionTask*)task{NSParameterAssert(task);NSParameterAssert(delegate);//加鎖操作[self.lock lock];//為Task設置與之代理方法關聯關系。通過一個字典self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;//添加對Task開始、重啟、掛起狀態的通知的接收。[selfaddNotificationObserverForTask:task];? ? [self.lock unlock];}/**
給Task添加任務開始、重啟、掛起的通知
@param task 任務
*/- (void)addNotificationObserverForTask:(NSURLSessionTask*)task {? ? [[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];? ? [[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];}
5 從下面開始,任務就正式開始執行。其實就是[downloadTask resume];執行以后開始。
/**
在網絡請求正式開始以后,這個方法會在數據接收的過程中多次調用。我們可以通過這個方法獲取數據下載的大小、總得大小、還有多少么有下載
@param session session
@param downloadTask 對應的Task
@param bytesWritten 已經下載的字節
@param totalBytesWritten 總的字節大小
@param totalBytesExpectedToWrite nil
*/- (void)URLSession:(NSURLSession*)session? ? ? downloadTask:(NSURLSessionDownloadTask*)downloadTask? ? ? didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{//獲取Task對應的`AFURLSessionManagerTaskDelegate`對象。從而可以調用對應的代理方法AFURLSessionManagerTaskDelegate *delegate = [selfdelegateForTask:downloadTask];if(delegate) {//調用`AFURLSessionManagerTaskDelegate`類中的代理方法。從而實現對于進度更新等功能。//會調用下面的那個方法[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];? ? }if(self.downloadTaskDidWriteData) {//如果有`downloadTaskDidWriteData`Block的實現,則在這個調用Block從而實現對下載進度過程的控制。self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);? ? }}//AFURLSessionManagerTaskDelegate里面的這個代理方法實現對進度的更新。- (void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask? ? ? didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{//AFURLSessionManagerTaskDelegate代理方法實現對下載進度的記錄self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;self.downloadProgress.completedUnitCount = totalBytesWritten;}
6 Task完成以后,會調用AFURLSessionManagerTaskDelegate對象的方法對返回的數據封裝。
//AFURLSessionManagerTaskDelegate里面的這個代理方法實現對數據的具體處理。- (void)URLSession:(__unusedNSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError*)error{//獲取Task對應的manager對象__strongAFURLSessionManager *manager =self.manager;//要封裝的responseObject對象。__blockidresponseObject =nil;? ? __blockNSMutableDictionary*userInfo = [NSMutableDictionarydictionary];? ? userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;//返回的數據。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。則調用這個Blockif(self.completionHandler) {self.completionHandler(task.response, responseObject, error);? ? ? ? ? ? }//發送一個指定Task結束的通知dispatch_async(dispatch_get_main_queue(), ^{? ? ? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];? ? ? ? ? ? });? ? ? ? });? ? }else{//正確數據封裝//在一個并行的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。則調用這個Blockif(self.completionHandler) {self.completionHandler(task.response, responseObject, serializationError);? ? ? ? ? ? ? ? }//發送通知dispatch_async(dispatch_get_main_queue(), ^{? ? ? ? ? ? ? ? ? ? [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];? ? ? ? ? ? ? ? });? ? ? ? ? ? });? ? ? ? });? ? }}
7 移除Task對應的通知和對應的AFURLSessionManagerTaskDelegate代理對象。
- (void)removeDelegateForTask:(NSURLSessionTask*)task {NSParameterAssert(task);? ? [self.lock lock];//移除Task對應的通知[selfremoveNotificationObserverForTask:task];//移除Task對應的`AFURLSessionManagerTaskDelegate`代理對象。[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];? ? [self.lock unlock];}//移除通知監聽- (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))];}
通過上面的過程,我們發現核心流程都是圍繞了NSRULSessionTask對象以及與之綁定的AFURLSessionManagerTaskDelegate對象執行的。我們通過在NSRULSessionTask對象的代理方法里面手動調用AFURLSessionManagerTaskDelegate對應的代理方法來實現對數據的處理和簡化代碼的作用,這個設計思路的確吊吊的。還有一些方法沒有涉及到,不過大同小異,基本過程就是這樣,就不一一解釋了。
3.2 AFURLSessionManager一些特殊模塊的說明
AFURLSeeesionManager實現了NSSecureCoding協議。讓manager可以歸檔解檔。
/**
在iOS8以及以上環境下,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;}/**
我們發現對象歸檔的時候,只歸檔了`NSURLSessionConfiguration`屬性。所以說歸檔接檔的時候所有Block設置、operation設置都會失效。
@param coder coder
*/- (void)encodeWithCoder:(NSCoder*)coder {? ? [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];}
同時,AFURLSessionManager也實現了NSCopying協議。通過協議的實現過程,我們發現也是只使用了NSURLSessionConfiguration屬性。和歸檔解檔一樣。
#pragma mark - 實現NSCopying協議。copy的NAURLSessionManager沒有復制任何與代理處理相關的Block- (instancetype)copyWithZone:(NSZone*)zone {return[[[selfclass] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];}
有的時候,我們的請求會返回302這個狀態碼,這個表示需要請求重定向到另一個url,我們可以下面這個代理方法里面決定對于重定向的處理,如果對completionHandler傳入nil,則會把response傳入重定向請求。另外,backgroundSession的Task不會調用下面這個代理方法,而是直接調用。
/**
有的時候,我們的請求會返回302這個狀態碼,這個表示需要請求重定向到另一個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的調用返回處理完成的request對象。if(self.taskWillPerformHTTPRedirection) {? ? ? ? redirectRequest =self.taskWillPerformHTTPRedirection(session, task, response, request);? ? }//這個調用是必須的,執行重定向操作。if(completionHandler) {? ? ? ? completionHandler(redirectRequest);? ? }}
創建NSRULSessionUplaodTask的時候,在某些系統上會出現bug。AFN已經幫我們處理好:
- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request fromFile:(NSURL*)fileURL progress:(void(^)(NSProgress*uploadProgress)) uploadProgressBlock completionHandler:(void(^)(NSURLResponse*response,idresponseObject,NSError*error))completionHandler{? ? __blockNSURLSessionUploadTask*uploadTask =nil;//用線程安全的方式創建一個dataTask。修復iOS8下面的bug。url_session_manager_create_task_safely(^{? ? ? ? uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];? ? });//用于處理uploadTask在iOS7環境下面有可能創建失敗的情況。如果attemptsToRecreateUploadTasksForBackgroundSessions為true。則嘗試重新創建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來控制對異步處理返回結果的控制。非常有借鑒意義。
#pragma mark -? 獲取當前session對應的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"];? ? ? ? }//這里發送一個信號量,讓semaphore變為1。此時表示tasks已經成功獲取。dispatch_semaphore_signal(semaphore);? ? }];//這里會一直等待信號量變為1。dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//返回Task。通過信號量控制,避免了方法結束的時候,tasks還沒有正常獲取的情況。returntasks;}
4 _AFURLSessionTaskSwizzling私有類的說明
在iOS7和iOS8及以上的系統,NSRULSessionTask的具體實現是不同的。我們目前知道的不同有:
NSURLSessionTasks是一個類簇。所以我們初始化一個Task的時候,我們并不只到初始化的到底是哪個子類。
簡單的通過[NSURLSessionTask class]并不會起作用。必須通過NSURLSession創建一個task對象。然后獲取他所在的類。
iOS7下面,下面代碼中的localDataTask對象的繼承關系是__NSCFLocalDataTask->__NSCFLocalSessionTask->__NSCFURLSessionTask。
在iOS8以及以上系統。下面代碼中的localDataTask對象的繼承關系是__NSCFLocalDataTask->__NSCFLocalSessionTask->NSURLSessionTask。
在iOS7下面__NSCFLocalSessionTask和__NSCFURLSessionTask實現了resume和suspend方法,同時最重要的是他不調用父類的實現。但是iOS8下面,只有NSURLSessionTask實現了resume和suspend。所以在iOS7的環境下,我們需要想辦法讓resume和suspend調用NSURLSessionTask的具體實現。
下面的代碼完美的向我們展示了一個向類添加方法,并且swizzle方法實現的過程。值得仔細琢磨。
/**
切換theClass類的`originalSelector`和`swizzledSelector`的實現
@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);}/**
動態給一個類添加方法
@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這個方法的實現。IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([selfclass],@selector(af_resume)));//獲取dataTask的具體類Class currentClass = [localDataTaskclass];//如果父類有resume方法。則改變方法的具體實現。while(class_getInstanceMethod(currentClass,@selector(resume))) {? ? ? ? ? ? Class superClass = [currentClass superclass];//找到類和父類的resume方法實現IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass,@selector(resume)));? ? ? ? ? ? IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass,@selector(resume)));if(classResumeIMP != superclassResumeIMP &&? ? ? ? ? ? ? ? originalAFResumeIMP != classResumeIMP) {//添加方法、然后轉換方法的實現[selfswizzleResumeAndSuspendMethodForClass:currentClass];? ? ? ? ? ? }? ? ? ? ? ? currentClass = [currentClass superclass];? ? ? ? }? ? ? ? [localDataTask cancel];? ? ? ? [session finishTasksAndInvalidate];? ? }}/**
主要是實現了為一個類添加方法、并且轉換添加方法和原來對應方法的實現。
@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方法的實現互換。af_swizzleSelector(theClass,@selector(resume),@selector(af_resume));? ? }//為theClass類添加一個af_suspend方法if(af_addMethod(theClass,@selector(af_suspend), afSuspendMethod)) {//把dataTask的suspend和af_suspend方法的實現互換。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`調用resume方法其實就是執行`af_resume`的具體實現。
*/- (void)af_resume {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];//這里其實就是調用dataTask的resume實現[selfaf_resume];if(state !=NSURLSessionTaskStateRunning) {//這里的self其實就是`NSRULSessionDataTask`對象[[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];? ? }}/**
在iOS7下面,`NSURLSessionDataTask`調用suspend方法其實就是執行`af_suspend`的具體實現。
*/- (void)af_suspend {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];//這里其實就是調用dataTask的suspend具體實現[selfaf_suspend];if(state !=NSURLSessionTaskStateSuspended) {//這里的self其實就是`NSRULSessionDataTask`對象[[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];? ? }}@end
5 總結
AFURLSessionManager通過對task設置一個AFURLSessionManagerTaskDelegate代理來處理繁雜的請求進度管理。從而降低了代碼的負責度。是代理模式的一個很好的實踐。
AFURLSessionManager通過私有類_AFURLSessionTaskSwizzling來修改iOS7和iOS8系統上面不同。是對于方法swizzle的一個成功和完整的實踐。
AFURLSessionManager通過添加各種Block,讓我們對請求過程有全方位的控制和處理。同時提供簡潔的api,把負責的處理全部封裝好。
作者:NS西北風