AFNetWorking3.2.0原始碼閱讀-AFURLSessionManager(二)
AFNetWorking3.2.0原始碼閱讀-AFURLSessionManager(二)
AFURLSessionManager.m 檔案內容解析
Define
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;
}
static void url_session_manager_create_task_safely(dispatch_block_t block) {
if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
// Fix of bug
// Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
// Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
這兩個方法是用C語言的寫法封裝了一個用來解決在iOS 8及之前task執行block時崩潰的問題。
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;
}
進度佇列,這個佇列用在請求結果返回後處理時,保證在處理返回結果的時候時是非同步的。
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;
}
請求完成處理事務組,將請求完成後的結果(無論成功還是失敗)回撥放在 url_session_manager_completion_group
中的 指定佇列或者主佇列中去做,對應結果的通知也是在這裡邊做的。
dispatch_group_t
一般我們是用來操作一組相關的任務,在其中做任務之間同步和資料傳遞操作,還可以方便我們管理一組任務的狀態,但是他在這裡並沒有做任何關於同步的動作,只是單純新增進去了,我們暫且猜測他會在其他地方會用到這些功能,或者更有深意。
AFURLSessionManagerTaskDelegate
<NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
這個類從名字和遵守的協議我們很容易得知它是一個網路請求的代理方法處理類
Property
@property (nonatomic, weak) AFURLSessionManager *manager;
被代理的manger
,weak
防止迴圈引用
@property (nonatomic, strong) NSMutableData *mutableData;
在NSURLSessionTaskDelegate
的URLSession:task:didCompleteWithError:
方法中拼接下載的資料。
// 上傳進度
@property (nonatomic, strong) NSProgress *uploadProgress;
// 下載進度
@property (nonatomic, strong) NSProgress *downloadProgress;
// 下載檔案儲存路徑
@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;
Life Cycle Method
Init
- (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];
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
{
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES;
progress.pausingHandler = ^{
[weakTask suspend];
};
#if AF_CAN_USE_AT_AVAILABLE
if (@available(iOS 9, macOS 10.11, *))
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
{
progress.resumingHandler = ^{
[weakTask resume];
};
}
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
初始化方法功能:
- _mutableData
_uploadProgress
_downloadProgress
初始化。
- _uploadProgress
_downloadProgress
新增resumingHandler
pausingHandler
block。
- _uploadProgress
_downloadProgress
新增對 fractionCompleted
key值變化的監聽並反饋到downloadProgressBlock
uploadProgressBlock
方法中。
NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
在這個代理方法中處理了請求響應
注意點:
- 1.使用userInfo
可變字典儲存responseSerializer
downloadFileURL
mutableData
error
responseObject
serializationError
, 並將其在錯誤/成功的時候發出AFNetworkingTaskDidCompleteNotification
通知
- 2.在錯誤/成功的回撥中使用dispatch_group_async
在指定或者預設的組中將處理放入指定或者主佇列做操作集中處理,
- 3,在呼叫完成回撥completionHandler
block後,在主執行緒中啟用非同步操作傳送1
中的userInfo到AFNetworkingTaskDidCompleteNotification
通知
NSURLSessionDataDelegate
// 收到data回撥
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
[self.mutableData appendData:data];
}
- 1,在這個方法中更新
downloadProgress
的totalUnitCount
completedUnitCount
,更新時會改變其fractionCompleted
,因在manager
初始化中對fractionCompleted
做了KVO,所以會觸發NSProgress Tracking。 - 2,返回資料拼接到
self.mutableData
。
- (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;
}
上傳資料的進度更新,套路同上一個方法
NSURLSessionDownloadDelegate
- (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;
}
更新重新(斷電續傳)下載進度,通過通知呼叫進度的回撥,套路同上
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
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];
}
}
}
}
完成下載回撥
- 1,downloadTaskDidFinishDownloading
block實現的情況下,通過它獲取到下載的downloadFileURL。
- 2,downloadFileURL
獲取成功將臨時檔案移動到downloadFileURL目錄中。
- 3,如果檔案移動成功傳送AFURLSessionDownloadTaskDidFailToMoveFileNotification
通知,將當前下載的downloadTask 作為Object,fileManagerError.userInfo
作為userInfo傳送出去。
_AFURLSessionTaskSwizzling
這裡因為!(這個bug)[https://github.com/AFNetworking/AFNetworking/pull/2702]
做了方法置換的處理,大體的原因時因為在iOS7,iOS8的時候NSURLSession的結構發生了變化
AFURLSessionManager @interface()
@property
屬性內容和.h檔案中的Set方法基本一致,不需過多解釋
在這我們可以看到一個寫法:
我們常見的一種場景
在@interface
中對外暴露一個屬性的只讀屬性,但是在@implementation
中需要對其進行更改。
AFN中的寫法是在@interface
中用(readonly)
修飾,在Extension
中使用readwrite
修飾
官方文件上也是這麼建議的 !(Use Class Extensions to Hide Private Information)[https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW3]
當然我們還有其他的方式
比如直接在@implementation
使用_iVar
進行更改操作
還可以使用 @synthesize someVar = _someVar
// TODO:1,readonly 和 readwrite修飾符做了什麼,使用readonly的時候有沒有產生set方法?
// 2,為什麼@synthesize someVar = _someVar
之後就可以更改被readonly修飾的屬性?
AFURLSessionManager @implementation
Init
// 重寫init,呼叫指定初始化方法`initWithSessionConfiguration`
- (instancetype)init {
return [self initWithSessionConfiguration:nil];
}
// 真正的初始化方法
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
// 預設configuration設定
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
// 操作佇列初始化,預設最大併發數為1
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
// session 初始化,使用剛剛初始化的操作佇列和sessionConfiguration
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
// 預設的響應格式化器為JSON
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 預設的加密設定
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
// 非WATCH環境設定網路監聽
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 設定 Task和代理關係的記錄dictionary
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// 初始化鎖
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 將當前session中的所有task設定對應的代理
[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;
}
dealloc
// 移除所有的通知監聽,釋放物件
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
addDelegateFor…Task
給data,upload,download task 設定代理等操作
// 設定data task
- (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 *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:uploadTask];
delegate.uploadProgressBlock = uploadProgressBlock;
}
- (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] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
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;
}
這三個方法大體步驟是一樣的,無非就是設定各種回撥block,方法代理等
有幾個點我們需要注意:
delegate.manager = self;
這裡delegate 是一個 AFURLSessionManagerTaskDelegate
的區域性變數,delegate.manager是一個AFURLSessionManager
型別的物件,在AFURLSessionManagerTaskDelegate
class中使用weak關鍵字描述,是為了迴圈引用,因為delegate.manager = self;
中self就是AFURLSessionManager
型別的物件。
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
.taskDescriptionForSessionTasks
是一個get方法
- (NSString *)taskDescriptionForSessionTasks {
return [NSString stringWithFormat:@"%p", self];
}
格式控制符“%p”中的p是pointer(指標)的縮寫,所以給task設定的描述是本類物件為一的指標值
[self setDelegate:delegate forTask:dataTask];
這個方法的實現:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
// 判空
NSParameterAssert(task);
NSParameterAssert(delegate);
// 鎖
[self.lock lock];
// 此字典記錄所有task和他的代理(delegate,一個AFURLSessionManagerTaskDelegate類物件)的對應關係,是一個key-value的格式
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 設定進度相關的操作
[delegate setupProgressForTask:task];
// 新增啟動和暫停的監聽
[self addNotificationObserverForTask:task];
// 開鎖
[self.lock unlock];
}
啟動/暫停
- (void)taskDidResume:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
// 是否有 `taskDescription`方法
if ([task respondsToSelector:@selector(taskDescription)]) {
// 如果有,是否是當前這個物件
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
// 如果是,傳送task resume 的通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}
- (void)taskDidSuspend:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
// 是否有`taskDescription`方法
if ([task respondsToSelector:@selector(taskDescription)]) {
// 如果有,是否是當前這個物件
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
// 如果是,傳送task suspend 的通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
});
}
}
}
get/remove Delegete For Task
// 獲取task對應的delegate , 是 `- setDelegate:foTask:` 方法的你操作,也會加鎖,因為在併發情況下,可能會出現資料同步問題
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
// 刪除task的delegate
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
// 判空
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[self.lock lock];
// 清除進度回撥
[delegate cleanUpProgressForTask:task];
// 清除監聽
[self removeNotificationObserverForTask:task];
// 在記錄表中清除task的記錄
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
獲取各種tasks
- (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)];
}
不難看出,核心方法是 -tasksForKeyPath:
- (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"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
這裡需要我們注意的是用到了dispatch的訊號量,這是因為getTasksWithCompletionHandler
是一個非同步操作:官方文件
invalidateSeesionCancelingTasks:
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks {
dispatch_async(dispatch_get_main_queue(), ^{
if (cancelPendingTasks) {
// 立即取消所有任務
[self.session invalidateAndCancel];
} else {
// 立即returns,但是並不會回影響正在執行的任務,它們會繼續執行到結束
[self.session finishTasksAndInvalidate];
}
});
}
// TODO:這裡有個放在主執行緒操作的程式碼,不知道是為了什麼著想,想必是為了安全,有大牛來給解答一下?
Send Request
data task request
// interface
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
}
- (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 {
__block NSURLSessionDataTask *dataTask = nil;
// 解決iOS 8 及其以下版本的bug
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
__block
修飾符的背後究竟發生了什麼,為什麼用它修飾就可以在block中更改?
為什麼不用它修飾就不能在block中更改?,這個問題我在之前的blog中寫過,請看:Block截獲自動變數實現與__block修飾符內部實現
upload task request
uploadTaskWithRequest:fromFile:
- (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 為 nil, 並且 self.attemptsToRecreateUploadTasksForBackgroundSessions == YES, 並且 self.session.configuration.identifier存在的情況下
// 嘗試重新建立uploadTask,最多嘗試3次
// 這個操作是因為在iOS7以下版本會出現background情況下upload有時會變成nil
if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
}
}
[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
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
對data,和stream的請求我們不用詳細探究
download task request
- (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
- (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
download task和upload task操作幾乎一摸一樣
get upload/download progress for task
- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
return [[self delegateForTask:task] uploadProgress];
}
- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
return [[self delegateForTask:task] downloadProgress];
}
看明白delegateForTask:
方法即可
NSObject
// 過載description方法便於在NSLog的時候獲取更具像化的資訊
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue];
}
// 過載是否可以響應selector的判斷方法,讓本類物件在沒有實現對應block時可以將方法響應繼續向下傳遞,而不是阻斷。
- (BOOL)respondsToSelector:(SEL)selector {
if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {
return self.taskWillPerformHTTPRedirection != nil;
} else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {
return self.dataTaskDidReceiveResponse != nil;
} else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {
return self.dataTaskWillCacheResponse != nil;
} else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
return self.didFinishEventsForBackgroundURLSession != nil;
}
return [[self class] instancesRespondToSelector:selector];
}
NSURLSessionDelegate
// 發生錯誤回撥
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
// 傳送錯誤通知
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
// 發生身份驗證回撥
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
// 如果實現了對身份驗證的block就執行
if (self.sessionDidReceiveAuthenticationChallenge) {
// 執行結果
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
// 如果驗證方式是 NSURLAuthenticationMethodServerTrust ,
// @abstract SecTrustRef validation required. Applies to any protocol.
// 這裡的驗證方式還有很多,請看 NSURLProtectionSpace.h 中的定義
// [瞭解更多看這裡](https://draveness.me/afnetworking5)
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 如果我們信任這個host
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
//獲取證書
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
// 如果有證書
if (credential) {
// 指定的證書,可以為nil
disposition = NSURLSessionAuthChallengeUseCredential;
} else { 如果沒有證書
// 使用預設的處理,如果這個代理方法沒有實現,將會忽略這個證書引數
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
// 拒絕
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
}
} else {
// 使用預設的處理,如果這個代理方法沒有實現,將會忽略這個證書引數
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
// 完成身份驗證後的回撥block
if (completionHandler) {
completionHandler(disposition, credential);
}
}
NSURLSessionTaskDelegate
// 處理重定向操作,
- (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);
}
}
// 這裡和NSURLSessionDelegate 中同樣有個認證回撥,區別就在於,這裡並沒有針對獲取證書是否成功做disposition的對應處理,看樣子是預設證書一定會獲取成功,這裡稍微有點懵逼,有大神指正沒有?
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
請求輸入流回調
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
NSInputStream *inputStream = nil;
// 如果self.taskNeedNewBodyStream實現
if (self.taskNeedNewBodyStream) {
inputStream = self.taskNeedNewBodyStream(session, task);
} else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
// 如果task.originalRequest.HTTPBodyStream存在 ,並且遵循了NSCopying協議
// 因為HTTPBodyStream不是執行緒安全的,所以在使用的時候需要copy
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
{
int64_t totalUnitCount = totalBytesExpectedToSend;
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
// 用KVC的方式獲取內容長度
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
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);
}
}
NSURLSessionDataDelegate
// 完成
- (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 (comple