1 概述
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 {
//通過預設配置初始化Session
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
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) {
return nil;
}
return request;
}];
}
//監聽網路狀態
[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"%ld",(long)status);
}];
[manager.reachabilityManager startMonitoring];
NSURL *URL = [NSURL URLWithString:bigPic];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
NSLog(@"下載進度:%lld",downloadProgress.completedUnitCount);
} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
NSLog(@"fileURL:%@",[fileURL absoluteString]);
return fileURL;
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:filePath]];
NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];
}
通過這個請求,我們發現AFURLSessionManager
要負責以下幾塊功能。
-
初始化和管理
NSURLSession
,通過它來建立和管理各種Task。 -
初始化和管理
NSRULSessionTask
,通過不同task來傳送不同請求。 -
管理各種認證功能、安全功能、請求重定向、資料處理。
-
管理和組織每個task的各種狀態管理和通知管理。不同task的回撥處理。
-
幫我們管理和處理了
NSRULSession
系列api的各種代理方法。簡化了我們的處理。
2 AFURLSessionManager的宣告分析
AFURLSessionManager
根據一個指定的NSURLSessionConfiguration
建立和管理一個NSURLSession
物件。並且這個物件實現了<NSURLSessionTaskDelegate>
, <NSURLSessionDataDelegate>
, <NSURLSessionDownloadDelegate>
, 和 <NSURLSessionDelegate>
這幾個協議的協議方法。同時實現NSSecureCoding
和NSCopying
來實現歸檔解檔和copy功能。
2.1 AFURLSessionManager
的初始化api
這些api主要用於初始化、安全策略、網路狀態監聽等:
interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
//指定的初始化方法、通過他來初始化一個Manager物件。
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)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 <AFURLResponseSerialization> responseSerializer;
//用於指定session的安全策略。用於處理信任主機和證書認證等。預設是`defaultPolicy`。
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
//觀測網路狀態的變化,具體可以看我的Demo用法。
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
@end
2.2 AFURLSessionManager
獲取Task的api
這部分api主要是任務的建立、任務的分類、任務完成佇列處理、特殊情況的任務重新建立等:
//當前session建立的所有Task,這個是下面三種task的總和。
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
//當前session建立的DataTask
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
//當前session建立的uploadTask
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
//當前session建立的downloadTask
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
//用於處理任務回撥的GCD物件,預設是dispatch_main_queue。
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
//用於處理任務回撥的GCD的group物件,如果不初始化、則一個預設的Group被使用。
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
//在iOS7的環境下,我們通過background模式的session建立的uploadTask有時會是nil,如果這個屬性是yes,AFN會嘗試再次建立uploadTask。
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
//廢除manager對應的Session。通過傳入的引數來決定是否立即取消已經用session發出去的任務。
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
2.3 AFURLSessionManager
為管理Task建立Block
AFURLSessionManager
提供了很多建立Task的api。並且提供了很多處理Task的Block。應該說著幾個api就是AFN為我們提供的最大價值,他把所有delegate方法細節都處理好。直接提供給我們一些最實用的api,我們就不用去管理session系列繁瑣的delegate方法了。
//建立一個NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
//建立一個NSURLSessionDataTask,並且能獲取上傳或者下載進度
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
//建立一個上傳Task,並且指定上傳檔案的路徑。
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
////建立一個上傳Task,並且指定上傳的資料。
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(nullable NSData *)bodyData
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
//建立一個uploadTask,然後上傳資料
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
//新建一個download任務,destination表示的下載檔案的快取路徑
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
//繼續恢復一個download任務。resumeData參數列示的是恢復下載的時候初始化資料,比如前面已經下載好的部分資料。
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
//獲取指定Task的上傳進度
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
//獲取指定Task的下載進度
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
注意:上面所有Task的progress都不在主執行緒、所以要在progress中做UI更新,都必須手動在主執行緒操作。
2.4 AFURLSessionManager
設定各種情況的代理回撥
這些回撥Block主要是用於處理網路請求過程或者結束以後的資料處理、認證、通知、快取等。我們可以通過設定這些Block來獲取或者檢測各種狀態。相當於就是鉤子函式。通過下面的這些Block,我們基本可以獲取請求過程中的所有狀態以及需要做的各種處理。
//設定Session出錯或者無效的手的回撥Block。這個Block主要在`NSURLSessionDelegate`代理的`URLSession:didBecomeInvalidWithError:`方法中執行。
- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block{
}
//當網路請需要的認證資訊比如使用者名稱密碼已經傳送了的時候,就可以通過這個Block來處理。這個Block是在`NSURLSessionDelegate`代理裡面的`URLSession:didReceiveChallenge:completionHandler:`方法中被執行。注意這個是針對Session
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block{
}
////當網路請需要的認證資訊比如使用者名稱密碼已經傳送了的時候,就可以通過這個Block來處理。這個Block是在`NSURLSessionTaskDelegate`代理裡面的`URLSession:task:didReceiveChallenge:completionHandler:`方法中被執行。注意這個是針對Task。
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block{
}
//當請求需要一個新的bodystream的時候,就可以通過這個Block來設定。這個Block在`NSURLSessionTaskDelegate` 代理協議的`URLSession:task:needNewBodyStream:`方法裡面設定。
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block{
}
//當一個網路請求需要重定向的時候。就會呼叫這個Block。這個Block是在`NSURLSessionTaskDelegate`協議的`URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`方法中呼叫的。
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block{
}
//可以通過設定這個Block來獲取上傳進度。這個Block主要在`NSURLSessionTaskDelegate`協議的 `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`方法中呼叫.
- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block{
}
//設定一個Task完成以後執行的Block,這個Block在`NSURLSessionTaskDelegate`協議的 `URLSession:task:didCompleteWithError:`方法中執行。
- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block{
}
//當接收到網路請求返回以後,可以呼叫這個Block。這個Block是在`NSURLSessionDataDelegate`協議的 `URLSession:dataTask:didReceiveResponse:completionHandler:`
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block{
}
//如果一個dataTask轉換為downLoadTask以後,就可以設定這個Block來呼叫。在`NSURLSessionDataDelegate` 協議的`URLSession:dataTask:didBecomeDownloadTask:`方法中呼叫。
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block{
}
//當dataTask接收到資料以後,可以設定呼叫這個Block。具體在`NSURLSessionDataDelegate`協議的`URLSession:dataTask:didReceiveData:`方法。
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block{
}
//設定一個Block來決定是否處理或者換成網路請求快取。具體在`NSURLSessionDataDelegate`協議的`URLSession:dataTask:willCacheResponse:completionHandler:`方法中。
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block{
}
//當session所有的任務都傳送出去以後,就可以通過這個Block來獲取。具體在`NSURLSessionDataDelegate`協議的 `URLSessionDidFinishEventsForBackgroundURLSession:`方法中。
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block{
}
//當一個downloadTask執行完畢以後,可以通過這個Block來獲取下載資訊,我們可以通過這個Block獲取下載檔案的位置。具體在`NSURLSessionDownloadDelegate`協議的`URLSession:downloadTask:didFinishDownloadingToURL:`方法中被呼叫。
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block{
}
//可以通過這個Block獲取一個downloadTask的下載進度。這個Block會在下載過程中多次被呼叫。具體是在`NSURLSessionDownloadDelegate`協議中的`URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`方法中被呼叫。
- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block{
}
//當一個downloadTask重新開始以後,我們可以通過這個Block獲取fileOffSet等資訊獲取已經下載的部分以及總共有多少要下載。具體是在`NSURLSessionDownloadDelegate`協議的`URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`方法中被呼叫。
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(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:[NSURLSessionConfiguration defaultSessionConfiguration]];
實現檔案中對應的處理如下:
/**
初始化方法
@return 返回一個manager物件
*/
- (instancetype)init {
return [self initWithSessionConfiguration:nil];
}
/**
預設初始化方法、通過這個方法來做manager的具體化初始化動作
@param configuration NSURLSession的配置
@return 返回一個manager物件
*/
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
//如果使用者沒有手動指定,則使用預設的configuration來初始化
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
//賦值給屬性
self.sessionConfiguration = configuration;
//初始化NSURLSession的task代理方法執行的佇列。
//這裡有一個很關鍵的點是task的代理執行的queque一次性只能執行一個task。這樣就避免了task的代理方法執行的混亂。
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
//出絲滑NSURLSession物件,最核心的物件。
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue: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 = [[NSMutableDictionary alloc] init];
//初始化一個鎖物件,關鍵操作加鎖。
self.lock = [[NSLock alloc] 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 *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
請求執行,介面檔案如下:
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
NSLog(@"下載進度:%lld",downloadProgress.completedUnitCount);
} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
NSLog(@"fileURL:%@",[fileURL absoluteString]);
return fileURL;
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
self.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:filePath]];
NSLog(@"File downloaded to: %@", filePath);
}];
實現檔案則呼叫了很多方法:
1 首先是初始化一個NSURLSessionDownLoadTask
物件
//通過session建立一個downloadTask,
__block NSURLSessionDownloadTask *downloadTask = nil;
//url_session_manager_create_task_safely作用是修復在iOS8下面的系統bug。
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
2 通過[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
這句話來為Task設定一個AFURLSessionManagerTaskDelegate
代理物件。從而可以實現對進度處理、Block呼叫、Task完成返回資料的拼裝的功能。
//根據指定的Task,初始化一個AFURLSessionManagerTaskDelegate
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
delegate.manager = self;
//設定Task完成的回撥Block
delegate.completionHandler = completionHandler;
if (destination) {
//任務完成以後,呼叫destination這個Block
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
//指定Task與taskDescriptionForSessionTasks的關聯關係,方便後面的通知中做對應的處理。
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
//新增通知
[self setDelegate:delegate forTask:downloadTask];
//設定一個下載進度的Block,以便在後面代理方法中呼叫。
delegate.downloadProgressBlock = downloadProgressBlock;
3 初始化一個AFURLSessionManagerTaskDelegate物件。在這個物件中對Task的請求過程進行處理和控制。
/**
初始化一個AFURLSessionManagerTaskDelegate物件
@param task 物件繫結的Task
@return 返回物件
*/
- (instancetype)initWithTask:(NSURLSessionTask *)task {
self = [super init];
if (!self) {
return nil;
}
//這個屬性用於儲存Task下載過程中的資料
_mutableData = [NSMutableData data];
//儲存Task上傳和下載的進度
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
{
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
//當progress物件取消的時候,取消Task
progress.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:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
//上面通過對fractionCompleted方法KVO。則會呼叫下面的方法,從而執行manager的
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) {
//更新下載進度Block
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}else if ([object isEqual:self.uploadProgress]) {
//更新上傳進度Bloc
if (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開始、重啟、掛起狀態的通知的接收。
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
/**
給Task新增任務開始、重啟、掛起的通知
@param task 任務
*/
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@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)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
//獲取Task對應的`AFURLSessionManagerTaskDelegate`物件。從而可以呼叫對應的代理方法
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask: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)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
//AFURLSessionManagerTaskDelegate代理方法實現對下載進度的記錄
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
self.downloadProgress.completedUnitCount = totalBytesWritten;
}
6 Task完成以後,會呼叫AFURLSessionManagerTaskDelegate
物件的方法對返回的資料封裝。
//AFURLSessionManagerTaskDelegate裡面的這個代理方法實現對資料的具體處理。
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
//獲取Task對應的manager物件
__strong AFURLSessionManager *manager = self.manager;
//要封裝的responseObject物件。
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//返回的資料。
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
//We no longer need the reference, so nil it out to gain back some memory.
self.mutableData = nil;
}
//如果是downloadTask,則封裝downloadFileURL
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (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。則呼叫這個Block
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
//傳送一個指定Task結束的通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {//正確資料封裝
//在一個並行的dispat_queuq_t物件裡面非同步處理。
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
//封裝responseBojct
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
//如果Task有完成Block。則呼叫這個Block
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
//傳送通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
7 移除Task對應的通知和對應的AFURLSessionManagerTaskDelegate
代理物件。
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
[self.lock lock];
//移除Task對應的通知
[self removeNotificationObserverForTask:task];
//移除Task對應的`AFURLSessionManagerTaskDelegate`代理物件。
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
//移除通知監聽
- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task];
}
//`AFURLSessionManagerTaskDelegate`物件回收。
- (void)dealloc {
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
通過上面的過程,我們發現核心流程都是圍繞了NSRULSessionTask
物件以及與之繫結的AFURLSessionManagerTaskDelegate
物件執行的。我們通過在NSRULSessionTask
物件的代理方法裡面手動呼叫AFURLSessionManagerTaskDelegate
對應的代理方法來實現對資料的處理和簡化程式碼的作用,這個設計思路的確吊吊的。還有一些方法沒有涉及到,不過大同小異,基本過程就是這樣,就不一一解釋了。
3.2 AFURLSessionManager一些特殊模組的說明
AFURLSeeesionManager
實現了NSSecureCoding
協議。讓manager可以歸檔解檔。
/**
在iOS8以及以上環境下,supportsSecureCoding必須重寫並且返回true。
@return bool
*/
+ (BOOL)supportsSecureCoding {
return YES;
}
//解檔
- (instancetype)initWithCoder:(NSCoder *)decoder {
NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];
self = [self initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
return self;
}
/**
我們發現物件歸檔的時候,只歸檔了`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 [[[self class] 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, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *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 (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
}
}
//為Task新增`AFURLSessionManagerTaskDelegate`代理方法
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
通過使用dispatch_semaphore_t
來控制對非同步處理返回結果的控制。非常有借鑑意義。
#pragma mark - 獲取當前session對應的task列表。通過dispatch_semaphore_t來控制訪問過程。
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *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;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([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還沒有正常獲取的情況。
return tasks;
}
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
*/
static inline void af_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
*/
static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
return class_addMethod(theClass, selector, method_getImplementation(method), method_getTypeEncoding(method));
}
@implementation _AFURLSessionTaskSwizzling
+ (void)load {
if (NSClassFromString(@"NSURLSessionTask")) {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
//初始化一個dataTask物件
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
//獲取af_resume這個方法的實現。
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
//獲取dataTask的具體類
Class currentClass = [localDataTask class];
//如果父類有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) {
//新增方法、然後轉換方法的實現
[self swizzleResumeAndSuspendMethodForClass: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");
return NSURLSessionTaskStateCanceling;
}
/**
在iOS7下面,`NSURLSessionDataTask`呼叫resume方法其實就是執行`af_resume`的具體實現。
*/
- (void)af_resume {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
//這裡其實就是呼叫dataTask的resume實現
[self af_resume];
if (state != NSURLSessionTaskStateRunning) {
//這裡的self其實就是`NSRULSessionDataTask`物件
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
}
/**
在iOS7下面,`NSURLSessionDataTask`呼叫suspend方法其實就是執行`af_suspend`的具體實現。
*/
- (void)af_suspend {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
//這裡其實就是呼叫dataTask的suspend具體實現
[self af_suspend];
if (state != NSURLSessionTaskStateSuspended) {
//這裡的self其實就是`NSRULSessionDataTask`物件
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
}
}
@end
5 總結
AFURLSessionManager
通過對task設定一個AFURLSessionManagerTaskDelegate
代理來處理繁雜的請求進度管理。從而降低了程式碼的負責度。是代理模式的一個很好的實踐。
AFURLSessionManager
通過私有類_AFURLSessionTaskSwizzling
來修改iOS7和iOS8系統上面不同。是對於方法swizzle的一個成功和完整的實踐。
AFURLSessionManager
通過新增各種Block,讓我們對請求過程有全方位的控制和處理。同時提供簡潔的api,把負責的處理全部封裝好。
原始碼地址iOSSourceCodeStudy,部落格地址部落格地址。