AFNetworking初探

我們先看一下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西北風

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

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