你要知道的NSURLSession都在這里
轉載請注明出處 http://www.lxweimin.com/p/167e7e2cbc9e
本系列文章主要講解iOS中網絡請求類NSURLSession的使用方法進行詳解,同時也會以此為擴展,講解SDWebImage中圖片下載功能的源碼分析,講解AFNetworking相關源碼分析。本系列文章主要分為以下幾篇進行講解,讀者可按需查閱。
- iOS網絡——NSURLSession詳解及SDWebImage源碼解析
- iOS網絡——SDWebImage SDImageDownloader源碼解析
- iOS網絡——AFNetworking AFURLSessionManager源碼解析
- iOS網絡——AFNetworking AFHttpSessionManager源碼解析
AFNetworking AFURLSessionManager源碼解析
前面幾篇文章講解了NSURLSession
的基礎使用方法以及SDWebImage
在下載圖片時是如何使用NSURLSession
,來完成下載任務的,本篇文章將閱讀AFNetworking
是如何封裝NSURLSession
來提供便捷的網絡請求功能。
AFNetworking
的使用方法就不贅述了,我們經常使用AFHttpSessionManager
來發起一個GET
或是POST
請求,該類是繼承自AFURLSessionManager
并在其上進行了一個封裝,提供了更加便捷的接口來發起網絡請求,所以本篇將詳細講解AFURLSessionManager
是如何封裝NSURLSession
的,建議讀者對照源碼閱讀。
首先看一下頭文件的聲明:
//AFURLSessionManager類實現了NSURLSession相關的眾多協議,用于幫助我們進行數據的處理
@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
//豬腳,NSURLSession 作為一個實例變量,AFN發起的網絡請求都是通過該session創建的task實現的
@property (readonly, nonatomic, strong) NSURLSession *session;
//NSURLSession delegate方法執行隊列
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
//響應序列化,不是本系列文章的關注點,有興趣的讀者可以自行閱讀源碼
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
///-------------------------------
/// @name Managing Security Policy
///-------------------------------
//安全策略,用于https等需要驗證的地方
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
#if !TARGET_OS_WATCH
///--------------------------------------
/// @name Monitoring Network Reachability
///--------------------------------------
//監測網絡連通性,使用AFNetworkReachabilityManager,讀者可自行閱讀相關源碼
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
#endif
///----------------------------
/// @name Getting Session Tasks
///----------------------------
//前面那個session管理的data upload download task的集合
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
//前面那個session管理的data task的集合
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
//前面那個session管理的upload task的集合
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
//前面那個session管理的download task的集合
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
///-------------------------------
/// @name Managing Callback Queues
///-------------------------------
//完成網絡請求后執行回調塊的隊列,如果為nil則使用主隊列
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
//完成網絡請求后回調塊的
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
///---------------------------------
/// @name Working Around System Bugs
///---------------------------------
//background類型的session是否嘗試重新創建上傳任務
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
///---------------------
/// @name Initialization
///---------------------
//初始化函數,根據指定NSURLSessionConfiguration創建session
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
//設置session無效,cancelPendingTasks標識是否取消session中正在執行的任務
//內部還是調用NSURLSession的invalidate方法
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
///-------------------------
/// @name Running Data Tasks
///-------------------------
//根據指定的request等信息創建一個NSURLSessionDataTask任務
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE;
//根據指定的request等參數創建一個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;
///---------------------------
/// @name Running Upload Tasks
///---------------------------
//根據指定request fileURL等參數構造一個NSURLSessionUploadTask任務
- (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;
//根據指定request和data等參數構造一個上傳任務
- (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;
//根據指定的request構造一個上傳任務
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
///-----------------------------
/// @name Running Download Tasks
///-----------------------------
//構造下載任務
- (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;
//構造下載任務
- (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;
///---------------------------------
/// @name Getting Progress for Tasks
///---------------------------------
//根據NSURLSessionTask獲取對應的任務完成進度NSProgress
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
//根據NSURLSessionTask獲取對應下載任務的進度NSProgress
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
///-----------------------------------------
/// @name Setting Session Delegate Callbacks
///-----------------------------------------
//設置session無效時執行的回調塊
- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
//設置session收到challenge時執行的回調塊
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
///--------------------------------------
/// @name Setting Task Delegate Callbacks
///--------------------------------------
//設置session需要新的流時執行的回調塊
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
//設置session的任務需要執行重定向時執行的回調塊
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * _Nullable (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
//又是代理方法執行時需要執行的回調塊
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
//同上
- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;
//同上
- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
///-------------------------------------------
/// @name Setting Data Task Delegate Callbacks
///-------------------------------------------
/**
Sets a block to be executed when a data task has received a response, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
@param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response.
*/
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;
//同上
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;
//同上
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;
//同上
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
//同上
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
///-----------------------------------------------
/// @name Setting Download Task Delegate Callbacks
///-----------------------------------------------
//同上
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
//同上
- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
//同上
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
@end
///--------------------
/// @name Notifications
///--------------------
//一些通知的名稱,使用FOUNDATION_EXPORT標識
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification;
FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;
FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey;
NS_ASSUME_NONNULL_END
通過頭文件可以發現,AFURLSessionManager
是封裝了NSURLSession
并實現了其相關的所有協議,提供了一系列方法用于構造各種網絡請求任務,并提供回調塊進行處理,還提供了一系列設置代理方法執行時的執行回調塊的方法,這樣,我們也可以監聽整個網絡請求的過程,當然也可以忽略這些代理方法執行情況,提供了各種通知,通過頭文件可以看出,主要目的還是為了封裝NSURLSession
從而提供更加便捷的方法來實現網絡請求。
接下來看一下實現的文件:
//C靜態函數,GCD只執行一次,用于創建一個串行隊列來執行各種網絡請求任務的創建工作
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
});
return af_url_session_manager_creation_queue;
}
/*
C靜態函數,用于執行創建網絡請求任務的block
主要目的是為了解決ios8以下存在的一個block和task不匹配的bug
上面那個隊列也是為了解決這個bug才創建的
具體可查看
Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
*/
static void url_session_manager_create_task_safely(dispatch_block_t block) {
if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
//C靜態函數,創建一個并發隊列,用于在網絡請求任務完成后處理數據的,并發隊列實現多線程處理多個請求完成后的數據處理
static dispatch_queue_t url_session_manager_processing_queue() {
static dispatch_queue_t af_url_session_manager_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_url_session_manager_processing_queue;
}
//C靜態函數創建一個dispatch的組
//但在接下來的源碼中并沒有使用這個組來實現notify等功能,僅僅是將block和組關聯了,不太清楚具體用意
//有明白的讀者還請不吝賜教
static dispatch_group_t url_session_manager_completion_group() {
static dispatch_group_t af_url_session_manager_completion_group;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_completion_group = dispatch_group_create();
});
return af_url_session_manager_completion_group;
}
//一系列通知名稱的定義
NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer";
NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
//需要使用的NSLock鎖的名稱
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
//background session重試創建上傳任務次數
static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
上面的代碼定義了一系列GCD隊列
,根據需求有串行隊列和并行隊列,串行隊列可以解決多線程情況下競爭條件的產生,并發隊列可以提高性能,每個隊列都有自己的功能,接下來會講到,接下來定義了各種通知的名稱。
typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);
typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask);
typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data);
typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse);
typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
上面是一系列回調塊的定義,在閱讀下面源碼的時候可以來找找看具體回調塊的參數。
//定義了一個類AFURLSessionManagerTaskDelegate并實現了NSURLSessionTask的相關協議
//通過名稱可以猜測,這個類是用于處理NSURLSessionTask相關代理方法的
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
//初始化構造函數,需要傳入一個關聯的task
- (instancetype)initWithTask:(NSURLSessionTask *)task;
//weak修飾的manager
@property (nonatomic, weak) AFURLSessionManager *manager;
//可變data用于存儲獲取到的網絡數據
@property (nonatomic, strong) NSMutableData *mutableData;
//上傳進度NSProgress
@property (nonatomic, strong) NSProgress *uploadProgress;
//下載進度NSProgress
@property (nonatomic, strong) NSProgress *downloadProgress;
//下載文件的NSURL
@property (nonatomic, copy) NSURL *downloadFileURL;
//下載完成的回調塊
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
//上傳進度的回調塊
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
//下載進度的回調塊
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
//網絡請求完成的回調塊
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
@end
@implementation AFURLSessionManagerTaskDelegate
//初始化構造函數
- (instancetype)initWithTask:(NSURLSessionTask *)task {
self = [super init];
if (!self) {
return nil;
}
_mutableData = [NSMutableData data];
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
//遍歷兩個上傳和下載NSProgress設置一些屬性
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
{
//初始化progress
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
//設置取消進度的回調塊,執行task的cancel方法
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES;
//設置暫停進度的回調塊,執行task的suspend方法
progress.pausingHandler = ^{
[weakTask suspend];
};
//設置重新開始的回調塊,執行task的resume方法
if ([progress respondsToSelector:@selector(setResumingHandler:)]) {
progress.resumingHandler = ^{
[weakTask resume];
};
}
//progress添加kvo,監聽progress的進度fractionCompleted
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
//析構函數
- (void)dealloc {
//刪除KVO
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
#pragma mark - NSProgress Tracking
//KVO回調方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
//上次或下載進度有改變時,執行上傳或下載進度回調塊
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
#pragma mark - NSURLSessionTaskDelegate
//代理方法,網絡請求完成或出錯
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//manager用weak修飾,這里strong一下防止manager被釋放
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
//為userInfo字典設置響應序列化
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
//賦值mutableData到data中,并釋放mutableData
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;
}
//如果downloadFileURL存在,即是下載任務就設置下載完成后的文件存儲url到字典中
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
//否則就設置對應的NSData數據到字典中
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
//如果網絡請求有錯誤
if (error) {
//設置error信息到字典中
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
/*
這個三目運算符需要解釋一下,在其他語言中這么寫很可能就是語法錯誤
這里的意思就是如果manager.completionGroup存在就使用它
不存在就使用url_session_manager_completion_group函數返回的group
后面的三目運算符同理
所以如果自己不設置manager的completionGroup或completionQueue就會使用默認提供的
*/
//執行對應的completionHandler回調塊
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
//在主隊列即主線程中發送通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {
//如果網絡任務成功完成,異步在并發隊列中執行數據處理
dispatch_async(url_session_manager_processing_queue(), ^{
//序列化響應數據
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
//如果是下載任務設置響應數據為文件的url
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
//如果響應對象序列化成功或是文件url就設置相關字典key-value
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
//如果序列化出錯,設置相關字典值
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
//同理,在dispatch組中和特定隊列執行回調塊
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
//主線程發送通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
#pragma mark - NSURLSessionDataDelegate
//回調方法,收到數據
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//設置下載進度的相關屬性
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
//添加數據到mutableData
[self.mutableData appendData:data];
}
//上傳任務的回調方法
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
//設置上傳進度的相關屬性
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.uploadProgress.completedUnitCount = task.countOfBytesSent;
}
#pragma mark - NSURLSessionDownloadDelegate
//下載任務的回調方法
//由于NSURLSession的downloadTask直接將文件下載到磁盤沙盒中,所以不需要mutableData自行接收數據
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
//設置下載進度的相關屬性
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
self.downloadProgress.completedUnitCount = totalBytesWritten;
}
//恢復下載任務的回調方法
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
//設置下載進度的相關屬性
self.downloadProgress.totalUnitCount = expectedTotalBytes;
self.downloadProgress.completedUnitCount = fileOffset;
}
//下載任務下載文件完成后的回調方法
//location就是文件下載到磁盤沙盒目錄的NSURL
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
//設置downloadFileURL為nil
self.downloadFileURL = nil;
//如果有下載完成的回調塊
if (self.downloadTaskDidFinishDownloading) {
//執行該回調塊,這個回調塊返回一個下載的文件保存的路徑URL
//默認保存在沙盒tmp文件中,可能會被刪除,需要持久化時要自己設置一個目錄存儲
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
//如果需要移動文件的路徑使用NSFileManaegr移動
if (self.downloadFileURL) {
NSError *fileManagerError = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
//文件移動發生錯誤發送通知
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
@end
上面這個類AFURLSessionManagerTaskDelegate
具體的源碼很簡單,主要就是關聯一個NSURLSessionTask
并實現了相關任務的代理方法用于數據的獲取、上傳文件和下載文件。需要注意的是NSURLSessionTask
的delegate
屬性是只讀的,只能在使用session
創建task
的函數中傳入代理對象,而且通過源碼也沒有發現相關代理的設置,所以AFURLSessionManagerTaskDelegate
類實現相關代理是為了其他類手動調用相關方法,AFURLSessionManager
也實現了這些代理,所以具體的調用是由AFURLSessionManger
手動觸發的。
接下來的源碼中有一部分關于method-swizzling
的技術,這一部分由于篇幅問題不打算展開講解,_AFURLSessionTaskSwizzling
類存在的目的就是為了交換NSURLSessionTask
的resume
和suspend
方法的實現,因為iOS7
和iOS8
中NSURLSessionTask
的父類不同,需要做一些處理,關于method-swizzling
技術有興趣的讀者可以閱讀iOS runtime探究(四): 從runtiem開始實踐Category添加屬性與黑魔法method swizzling
接下來看一下AFURLSessionManager
類的實現部分。
@interface AFURLSessionManager ()
//管理的session運行模式,默認情況下使用默認運行模式,defaultConfiguration
@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
/*
NSOperation隊列,代理方法執行的隊列
.h文件里是readonly,所以這里定義一個readwrite用于賦值
*/
@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
//管理的session,readwrite
@property (readwrite, nonatomic, strong) NSURLSession *session;
//可變字典,key是NSURLSessionTask的唯一NSUInteger類型標識,value是對應的AFURLSessionManagerTaskDelgate對象
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
//只讀屬性,通過getter返回數據
@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
//NSLock鎖
@property (readwrite, nonatomic, strong) NSLock *lock;
//下面是一系列回調塊
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
@end
上面就是定義了一系列的回調塊和一些屬性。
@implementation AFURLSessionManager
//構造函數
- (instancetype)init {
return [self initWithSessionConfiguration:nil];
}
//構造函數
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
//如果沒有指定session運行模式就使用默認的
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
//創建代理方法執行的隊列,最大并發數為1,即串行隊列
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
//創建session,注意代理對象是self
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
//創建響應序列化器
self.responseSerializer = [AFJSONResponseSerializer serializer];
//設置默認安全策略
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
//獲取網絡可達性manager
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//創建鎖
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
/*
獲取session中的任務,并調用相關方法關聯AFURLSessionManagerTaskDelegate
不太明白這里為什么要這么做,剛創建的session應該沒有任何任務在執行
*/
[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;
}
//析構方法,移除所有通知監聽
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
初始化方法中不太理解的地方就是在創建完session
后就去獲取它正在執行的相關任務,但是剛創建的session
為什么會存在正在執行的任務呢?如果有讀者明白還請不吝賜教。
//taskDescriptionForSessionTasks屬性的getter,返回地址的字符串形式數據,可以保證這個字符串是唯一的
- (NSString *)taskDescriptionForSessionTasks {
return [NSString stringWithFormat:@"%p", self];
}
//通知的回調方法,接下來的代碼會添加相關通知
- (void)taskDidResume:(NSNotification *)notification {
//發送通知的時候會將task添加進通知中
NSURLSessionTask *task = notification.object;
//判斷這個任務是否是當前manager管理的,如果是就發送相關通知
//task的taskDescription屬性在下文的源碼中會設置
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}
//同上
- (void)taskDidSuspend:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
});
}
}
}
上面的代碼就是通知的回調方法,用于通知resume和suspend事件。
//根據task獲取相關聯的AFURLSessionManagerTaskDelegate對象
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
//task不能為空
NSParameterAssert(task);
//上鎖,通過task的唯一taskIdentifier從字典中取值,這個唯一標識是在創建task的時候NSURLSessionTask為其設置的,不需要手動設置,保證唯一性
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
//為task設置關聯的delegate
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
//task和delegate都不能為空
NSParameterAssert(task);
NSParameterAssert(delegate);
//上鎖,向字典中添加key-value對
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
//重點方法,為dataTask創建一個關聯的AFURLSessionManagerTaskDelegate對象
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
//創建AFURLSessionManagerTaskDelegate
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
//設置相關屬性
delegate.manager = self;
delegate.completionHandler = completionHandler;
/*
設置task的taskDescription,注意和taskIdentifier區分
taskDescription是開發者自行設置的
taskIdentifier是NSURLSessionTask設置的,保證每一個task的id不同
這里設置的taskDescription就是AFURLSessionManager的地址
所以同一個manager創建的task的description都是一致的
設置這個值的目的就是為了區分task是否是當前manger創建的
*/
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//調用上面的方法將task-delegate鍵值對添加進字典中
[self setDelegate:delegate forTask:dataTask];
//設置回調塊
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
//同上,創建上傳任務的AFURLSessionManagerTaskDelegate對象,并加入到字典中
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:uploadTask];
delegate.uploadProgressBlock = uploadProgressBlock;
}
//同上,創建下載文件任務的AFURLSessionManagerTaskDelegate對象,并加入到字典中
- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
/*
需要注意下,AFURLSessionManagerTaskDelegate中下載文件完成后會調用delegate.downloadTaskDidFinishDownloading回調塊
來獲取下載文件要移動到的目錄URL
所以這里就是創建這個回調塊,直接返回參數中的destination回調塊
*/
if (destination) {
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:downloadTask];
delegate.downloadProgressBlock = downloadProgressBlock;
}
//從字典中刪除task對應的delegate的key-value對
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
[self.lock lock];
[self removeNotificationObserverForTask:task];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
上面的代碼就是對AFURLSessionManagerTaskDelegate
的創建、添加進字典、刪除、獲取的操作,這樣就實現了每一個NSURLSessionTask
對應一個AFURLSessionManagerTaskDelegate
對象,可能讀者會有疑問,AFURLSessionManager
既然已經實現了代理的方法,為什么不直接使用它來處理代理方法,為什么要創建一個類來專門處理,繼續看完源碼可能你就會明白了。
//根據keyPath獲取不同類型任務的集合
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
//創建一個信號量,值是0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//這個方法是異步的,所以為了同步返回結果,需要使用鎖,信號量值設置為0或者1時就可以當鎖來使用了
[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"];
}
//signal通知信號量,信號量值加1
dispatch_semaphore_signal(semaphore);
}];
//等待信號量,直到值大于0,等待時間是forever
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
//下面是tasks、dataTasks、uploadTasks、downloadTasks屬性的getter,都是調用上述方法來獲取對應類型的任務集合
- (NSArray *)tasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)dataTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)uploadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)downloadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
#pragma mark -
//設置session無效,根據參數判斷是否需要取消正在執行的任務
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks {
//調用NSURLSession對應的方法來設置session無效,同時打破引用循環
if (cancelPendingTasks) {
[self.session invalidateAndCancel];
} else {
[self.session finishTasksAndInvalidate];
}
}
#pragma mark -
//responseSerializer的setter
- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer {
NSParameterAssert(responseSerializer);
_responseSerializer = responseSerializer;
}
#pragma mark -
//添加通知,taskDidResume、taskDidSuspend方法前面講過了
- (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];
}
//刪除通知
- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task];
}
上面的方法是一些getter和setter,很簡單,不再贅述。
//創建并返回NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler: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 {
//為了解決iOS8一下的一個bug,調用一個串行隊列來創建dataTask
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
//使用session來創建一個NSURLSessionDataTask對象
dataTask = [self.session dataTaskWithRequest:request];
});
//為這個task創建一個AFURLSessionManagerTaskDelegate并關聯加入字典中
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
#pragma mark -
//創建一個NSURLSessionUploadTask對象
- (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;
url_session_manager_create_task_safely(^{
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
});
// uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113)
//解決iOS7的一個bug按照配置的嘗試次數創建上傳任務,默認嘗試3次
if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
}
}
//創建關聯的delegate并添加到字典中
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
//同上
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
url_session_manager_create_task_safely(^{
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
});
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
//同上
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
url_session_manager_create_task_safely(^{
uploadTask = [self.session uploadTaskWithStreamedRequest:request];
});
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
#pragma mark -
//創建下載任務,同上
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
//創建下載任務,同上
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithResumeData:resumeData];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
上面的方法就是AFURLSessionManager
為我們提供的獲取NSURLSessionDataTask
、NSURLSessionUploadTask
和NSURLSessionDownloadTask
的方法,上面這些方法主要目的就是傳入進度或完成回調塊,然后構造一個AFURLSessionManagerTaskDeleagte
對象并關聯,這樣就不需要開發者自行實現和管理代理方法做相關數據處理,只需要在回調塊中做處理即可。
接下來源碼中是一系列回調塊的setter方法,就不列下來了,讀者可以自己看看。接下來就講解重點的各種代理的回調方法。
//session無效后的回調方法
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
//如果回調塊存在就執行回調塊
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
//發送對應通知
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
//收到服務端的challenge,例如https需要驗證證書等
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
//如果有對應回調塊就執行
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
//處理https
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
上面兩個方法是NSURLSessionDelegate
的方法,關于驗證HTTPS
的部分代碼可以當做模板代碼來寫,具體內容不是本文講解范疇,讀者可自行查閱。
//要執行重定向的代理方法
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
{
//套路就是執行用戶自定義的回調塊,執行完成回調塊
NSURLRequest *redirectRequest = request;
if (self.taskWillPerformHTTPRedirection) {
redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
}
if (completionHandler) {
completionHandler(redirectRequest);
}
}
//同前面一樣,處理https鏈接
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.taskDidReceiveAuthenticationChallenge) {
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
//處理需要一個新的流
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
//調用用戶自定義的回調塊來獲取,或者copy一個
NSInputStream *inputStream = nil;
if (self.taskNeedNewBodyStream) {
inputStream = self.taskNeedNewBodyStream(session, task);
} else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
inputStream = [task.originalRequest.HTTPBodyStream copy];
}
if (completionHandler) {
completionHandler(inputStream);
}
}
//上傳任務的回調方法
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
//獲取上傳的總大小,如果數據不正確就從http首部中獲取
int64_t totalUnitCount = totalBytesExpectedToSend;
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
//獲取task關聯的AFURLSessionManagerTaskDelegate對象
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
//如果代理對象存在,就調用代理對象的這個方法
if (delegate) {
[delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
}
//如果用戶自定義回調塊存在,執行回調塊
if (self.taskDidSendBodyData) {
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
}
}
//任務完成的回調方法
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//同樣的套路,獲取關聯的代理對象,手動調用代理對象的這個方法,執行用戶自定義的回調塊
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
以上代碼是NSURLSessionTaskDelegate
的回調方法,通過上面的代碼可以發現AFURLSessionManagerTaskDelegate
的作用了,AFURLSessionManager
的代理方法中會根據task
獲取到對應的delegate
,如果需要提前處理一些數據就先處理,處理完成后手動觸發delegate
中的對應方法,然后具體的數據處理就交由AFURLSessionManagerTaskDelegate
來處理。繼續看其他代理方法:
//收到服務端響應的代理回調方法
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
//調用用戶自定義回調塊,執行完成回調塊
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
if (self.dataTaskDidReceiveResponse) {
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
if (completionHandler) {
completionHandler(disposition);
}
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
if (delegate) {
[self removeDelegateForTask:dataTask];
[self setDelegate:delegate forTask:downloadTask];
}
if (self.dataTaskDidBecomeDownloadTask) {
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//獲取代理,然后調用代理的這個方法,有自定義回調塊就執行
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
{
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.dataTaskWillCacheResponse) {
cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.didFinishEventsForBackgroundURLSession) {
dispatch_async(dispatch_get_main_queue(), ^{
self.didFinishEventsForBackgroundURLSession(session);
});
}
}
上面的代碼是NSURLSessionDataDelegate
的代理方法,同樣的,如果AFURLSessionManagerTaskDelegate
能響應的關于數據處理的方法都會通過task
找到對應delegate
后調用其對應的方法,然后執行用戶自定義的回調塊,如果代理不能響應的方法就由AFURLSessionManager
自行處理。
//下載任務下載文件完成后的回調方法
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
//獲取對應的代理對象
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
//如果downloadTaskDidFinishDownloading回調塊存在就執行它來獲取一個保存文件的URL路徑
if (self.downloadTaskDidFinishDownloading) {
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
//如果這個路徑存在就通過NSFileManager來移動,移動失敗發送通知
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
//通過task獲取delegate
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
//如果delegate存在就調用其該方法
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
}
//如果回調塊存在就執行
if (self.downloadTaskDidWriteData) {
self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
}
//同上套路
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
}
if (self.downloadTaskDidResume) {
self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
}
}
到現在為止,AFURLSessionManager
所有源碼都看完了,代碼封裝了NSURLSession
并提供了簡潔的創建NSURLSessionDataTask
、NSURLSessionUploadTask
和NSURLSessionDownloadTask
對象的方法,使用人員可以不需要考慮具體的數據處理過程,最簡單的可以只通過回調塊來獲取網絡請求的各種信息。在具體實現上,AFURLSessionManager
通過定義AFURLSessionManagerTaskDelegate
來做具體task
的數據處理,而AFURLSessionManager
只關注于通用部分的實現,并提供各種方法和回調塊用于處理task
,使得代碼結構更清晰,AFURLSessionManager
代理方法結構也更簡單。
通過源碼閱讀也可以學到AFURLSessionManager
是如何使用和管理NSURLSession
,以及相關回調方法具體實現細節,使用一個類就封裝了三種任務的處理方法,所以可以很便捷的使用,在開發中我們經常使用AFHTTPSessionManager
,其是AFURLSessionManager
的子類,所以,閱讀完AFURLSessionManager
源碼后,我們可以直接使用AFURLSessionManager
就能很方便的進行網絡請求,舉個栗子如下:
- (void)viewWillAppear:(BOOL)animated
{
//創建一個AFURLSessionManager對象,其中管理的session運行在默認模式下
AFURLSessionManager *manager = [[AFURLSessionManager alloc] init];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
//獲取一個NSURLSessionDataTask對象,訪問百度
NSURLSessionDataTask *task = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
//請求完成后的回調
NSLog(@"receive %@ %@ %@", response, responseObject, error);
}];
//啟動任務
[task resume];
//創建一個下載tomcat的request
NSURLRequest *downloadRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.1/bin/apache-tomcat-9.0.1.zip"]];
//創建一個下載任務
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:downloadRequest progress:^(NSProgress * _Nonnull downloadProgress) {
//每下載一包數據就輸出一下完成情況
NSLog(@"Download %lf", downloadProgress.fractionCompleted);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
//演示代碼不需要存儲在磁盤中,返回nil,如有需要自行指定目錄
return nil;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
//完成后輸出相關信息
NSLog(@"Download Complete %@ %@ %@", response, filePath, error);
}];
//啟動下載任務
[downloadTask resume];
}
上面的栗子很簡單,就不再贅述了,有興趣的讀者可以打斷點看一下執行過程是不是如源碼中講解的那般。
備注
由于作者水平有限,難免出現紕漏,如有問題還請不吝賜教。