源碼閱讀:AFNetworking(六)——AFURLSessionManager

該文章閱讀的AFNetworking的版本爲3.2.0。html

完成了前面對AFURLRequestSerializationAFURLResponseSerializationAFSecurityPolicyAFNetworkReachabilityManager類的閱讀,接下來就能夠閱讀AFNetworking的核心類AFURLSessionManager啦。經過一開始對頭文件的引用,就能夠知道AFURLSessionManager類的核心地位:ios

#import "AFURLResponseSerialization.h"
#import "AFURLRequestSerialization.h"
#import "AFSecurityPolicy.h"
#if !TARGET_OS_WATCH
#import "AFNetworkReachabilityManager.h"
#endif
複製代碼

以前對上述類的閱讀,就是爲了給本類閱讀作鋪墊。git

1.接口文件

1.1.屬性

/**
 網絡會話管理者,屬性是隻讀的。想要了解NSURLSession的使用能夠看這篇文章:使用NSURLSession(https://www.jianshu.com/p/fafc67475c73)
 */
@property (readonly, nonatomic, strong) NSURLSession *session;

/**
 操做隊列,也是隻讀的,最大併發被設置爲1,用於代理回調
 */
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;

/**
 響應數據序列化對象,默認爲`AFJSONResponseSerializer`,即處理json類型數據,不能設置爲`nil`
 */
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;

/**
 用於確保安全鏈接的安全策略對象,默認爲`defaultPolicy`
 */
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

/**
 網絡情況監控管理者,默認爲`sharedManager`
 */
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
複製代碼
  • 獲取Session的Tasks
/**
 當前`session`建立的全部的任務,至關於下面三個屬性的和
 */
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;

/**
 當前`session`建立的全部的`dataTasks`
 */
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;

/**
 當前`session`建立的全部的`uploadTasks`
 */
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;

/**
 當前`session`建立的全部的`downloadTasks`
 */
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
複製代碼
  • 管理回調隊列
/**
 任務回調隊列,默認或者爲NULL時,就是在主隊列
 */
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;

/**
 任務回調組,默認或者爲NULL時,就生成一個私有隊列
 */
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
複製代碼
  • 解決系統錯誤
/**
 從iOS 7.0開始,建立後臺上傳任務有時候會返回爲nil,若是設置爲YES,AFNetworking遵循了蘋果的建議,若是建立失敗會從新建立,默認嘗試三次。默認爲NO
 */
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
複製代碼

1.2.方法

  • 初始化方法
/**
 經過指定NSURLSessionConfiguration對象初始化對象
 */
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

/**
 當傳YES,就當即關閉當前網絡會話;當傳NO,等待當前任務完成後再關閉當前對話
 */
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
複製代碼
  • 建立NSURLSessionDataTask對象方法
/**
 以指定的NSURLRequest對象建立NSURLSessionDataTask
 */
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE;

/**
 以指定的NSURLRequest對象建立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;
複製代碼
  • 建立NSURLSessionUploadTask對象方法
/**
 以指定的NSURLRequest對象和要上傳的本地文件的URL建立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;

/**
 以指定的NSURLRequest對象和要上傳的二進制類型數據建立NSURLSessionUploadTask,上傳進度的回調也是在子線程中
 */
- (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;

/**
 以指定數據流的NSURLRequest對象建立NSURLSessionUploadTask,上傳進度的回調也是在子線程中
 */
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                 progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                        completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
複製代碼
  • 建立NSURLSessionDownloadTask對象方法
/**
 以指定的NSURLRequest對象建立NSURLSessionDownloadTask,上傳進度的回調是在子線程中,在下載的過程當中會先將文件放到一個臨時的路徑targetPath,在下載完成後,會將文件移動到用戶設置的路徑上,並自動刪除原路徑上的文件。
 可是,若是當初建立session的參數configuration設置的是後臺模式的話,在應用被殺死時destination中的信息會丟失,因此最好在-setDownloadTaskDidFinishDownloadingBlock:方法中設置下載文件的保存路徑
 */
- (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,恢復下載,其餘的和上面的方法相同
 */
- (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;
複製代碼
  • 獲取Tasks進度方法
/**
 獲取指定task的上傳進度
 */
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;

/**
 獲取指定task的下載進度
 */
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
複製代碼
  • 設置NSURLSessionDelegate回調方法
/**
 設置當session無效時的block回調,這個block回調用在NSURLSessionDelegate的方法URLSession:didBecomeInvalidWithError:中
 */
- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;

/**
 設置當session接收到驗證請求時的block回調,這個block回調用在NSURLSessionDelegate的方法URLSession:didReceiveChallenge:completionHandler:中
 */
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
複製代碼
  • 設置NSURLSessionTaskDelegate回調方法
/**
 設置當task須要一個新的輸入流時的block回調,這個block回調用在NSURLSessionTaskDelegate的方法URLSession:task:needNewBodyStream:中
 */
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;

/**
 設置當一個HTTP請求試圖執行重定向到一個不一樣的URL時的block回調,這個block回調用在NSURLSessionTaskDelegate的方法URLSession:willPerformHTTPRedirection:newRequest:completionHandler:中
 */
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * _Nullable (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;

/**
 設置當task接收到身份驗證時的block回調,這個block回調用在NSURLSessionTaskDelegate的方法URLSession:task:didReceiveChallenge:completionHandler:中
 */
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))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;
複製代碼
  • 設置NSURLSessionDataDelegate回調方法
/**
 設置當dataTask接收到響應時的block回調,這個block回調用在NSURLSessionDataDelegate的方法URLSession:dataTask:didReceiveResponse:completionHandler:中
 */
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;

/**
 設置當dataTask變成downloadTask時的block回調,這個block回調用在NSURLSessionDataDelegate的方法URLSession:dataTask:didBecomeDownloadTask:中
 */
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;

/**
 設置當dataTask接收到數據時的block回調,這個block回調用在NSURLSessionDataDelegate的方法URLSession:dataTask:didReceiveData:中
 */
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;

/**
 設置當dataTask將要對響應進行緩存時的block回調,這個block回調用在NSURLSessionDataDelegate的方法URLSession:dataTask:willCacheResponse:completionHandler:中
 */
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;

/**
 設置當session隊列中全部的消息都發送出去時的block回調,這個block回調用在NSURLSessionDataDelegate的方法URLSessionDidFinishEventsForBackgroundURLSession:中
 */
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
複製代碼
  • 設置NSURLSessionDownloadDelegate回調方法
/**
 設置當downloadTask完成一個下載時的block回調,這個block回調用在NSURLSessionDownloadDelegate的方法URLSession:downloadTask:didFinishDownloadingToURL:中
 */
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable  (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;

/**
 設置一個block回調來按期跟蹤下載進度,這個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回調,這個block回調用在NSURLSessionDownloadDelegate的方法URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:中
 */
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
複製代碼

1.3.全局靜態常量

  • 通知名稱
/**
 當一個task從新開始時就會發送這個通知
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification;

/**
 當一個task執行完成時就會發送這個通知
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification;

/**
 當一個task暫停時就會發送這個通知
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification;

/**
 當一個session無效時就會發送這個通知
 */
FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;

/**
 當sessionDownloadTask將下載在臨時路徑的文件移動到用戶指定路徑出錯時就是發送這個通知
 */
FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification;
複製代碼
  • 通知所傳遞的userInfo字典類型數據的key
/**
 經過這個key能夠從AFNetworkingTaskDidCompleteNotification通知所傳遞的userInfo字典類型數據中取出任務響應的原始數據
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey;

/**
 若是響應已經序列化,經過這個key能夠從AFNetworkingTaskDidCompleteNotification通知所傳遞的userInfo字典類型數據中取出任務響應的序列化數據
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey;

/**
 若是響應序列化對象,經過這個key能夠從AFNetworkingTaskDidCompleteNotification通知所傳遞的userInfo字典類型數據中取出任務響應序列化對象
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey;

/**
 若是相應數據已經直接存儲到磁盤,經過這個key能夠從AFNetworkingTaskDidCompleteNotification通知所傳遞的userInfo字典類型數據中取出下載數據的存儲路徑
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey;

/**
 若是存在錯誤,,經過這個key能夠從AFNetworkingTaskDidCompleteNotification通知所傳遞的userInfo字典類型數據中取出與task或者是響應序列化相關的錯誤
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey;
複製代碼

2.實現文件

2.1.宏定義

#ifndef NSFoundationVersionNumber_iOS_8_0
#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11
#else
#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
#endif
複製代碼

這個宏經過NSFoundation的版原本判斷當前系統的版本,總之就是將NSFoundationVersionNumber_With_Fixed_5871104061079552_bug定義爲iOS8.0github

2.2.靜態方法

/**
 建立一個單例串行隊列,用於建立task
 */
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;
}

/**
 安全的建立一個任務,主要是爲了兼容iOS8以前的系統bug,具體的緣由在第一篇「AFNetworking源碼閱讀(一)——從使用入手」文章中有解釋
 */
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();
    }
}

/**
 建立一個單利併發隊列,用於處理返回的數據
 */
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;
}

/**
 建立一個單利組,用於處理回調,用戶可經過dispatch_group_notify實現對回調完成的監控
 */
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;
}
複製代碼

2.3.全局靜態常量

  • 這些是對.h文件中全局靜態常量進行賦值
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";
複製代碼
  • 爲鎖對象命名
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
複製代碼
  • 這個就是在.h文件解釋屬性attemptsToRecreateUploadTasksForBackgroundSessions中提到的嘗試三次
static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
複製代碼

2.4.別名

  • 這些都是爲.h文件中設置的回調方法中爲接收傳入的block起的別名
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);
複製代碼
  • 這個爲進度回調block起的別名
typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
複製代碼
  • 這個爲完成回調block起的別名
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
複製代碼

2.5.AFURLSessionManagerTaskDelegate私有類

2.5.1.接口

  • 屬性
/**
 網路會話管理者,爲了防止循環引用這裏用了weak
 */
@property (nonatomic, weak) AFURLSessionManager *manager;

/**
 保存接收到的數據
 */
@property (nonatomic, strong) NSMutableData *mutableData;

/**
 上傳進度
 */
@property (nonatomic, strong) NSProgress *uploadProgress;

/**
 下載進度
 */
@property (nonatomic, strong) NSProgress *downloadProgress;

/**
 下載保存路徑
 */
@property (nonatomic, copy) NSURL *downloadFileURL;

/**
 保存下載完成回調block
 */
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;

/**
 保存上傳進度回調block
 */
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;

/**
 保存下載進度回調block
 */
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;

/**
 保存任務完成回調block
 */
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
複製代碼
  • 方法
/**
 以指定NSURLSessionTask對象初始化方法
 */
- (instancetype)initWithTask:(NSURLSessionTask *)task;
複製代碼

2.5.2.實現

  • 生命週期方法
- (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];
    
    // 將傳入的task的取消、暫停和重啓與進度對象相應的操做進行綁定
    __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 __has_warning("-Wunguarded-availability-new")
        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;
}

- (void)dealloc {
    // 移除進度對象的觀察
    [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
複製代碼
  • KVO方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    // 當進度對象的進度發生改變時,回調對應的block
    if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    } else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}
複製代碼
  • NSURLSessionTaskDelegate方法實現
/**
 當`NSURLSessionTaskDelegate`的這個方法被調用時,意味着這個`task`已經執行完成,不管是否出錯
 */
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    // 由於self.manager屬性關鍵字是weak,因此爲了防止被釋放就用__strong
    __strong AFURLSessionManager *manager = self.manager;

    // 用來保存請求返回的數據,爲了能夠在block中進行修改,用了__block
    __block id responseObject = nil;

    // 用來保存發送通知時傳遞的數據,爲了能夠在block中進行修改,用了__block,並進行賦值
    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    // 使用臨時變量保存請求到的數據,並把保存數據的屬性清空,節約內存
    //Performance Improvement from #2672
    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;
    }

    // 若是設置了下載文件的保存路徑,就傳遞保存路徑,不然若是有請求到的數據,就傳遞請求到的數據
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    // 若是請求出錯
    if (error) {
        // 傳遞錯誤信息
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

        // 用戶能夠自定義調度組和隊列並利用dispatch_group_notify實現對回調完成的監控
        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];

            // 若是有保存下載文件的路徑則返回路徑
            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(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, serializationError);
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
}
複製代碼
  • NSURLSessionDataDelegate方法實現
/**
 當`NSURLSessionDataDelegate`的這個方法被調用時,意味着已經接收到服務器返回的數據了
 */
- (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];
}

/**
 當`NSURLSessionDataDelegate`的這個方法被調用時,意味着已經向服務器上傳了數據
 */
- (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方法實現
/**
 當執行下載任務時,`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;
}

/**
 當`NSURLSessionDataDelegate`的這個方法被調用時,表示下載已重啓
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
    
    // 更新下載進度對象的屬性
    self.downloadProgress.totalUnitCount = expectedTotalBytes;
    self.downloadProgress.completedUnitCount = fileOffset;
}

/**
 當`NSURLSessionDataDelegate`的這個方法被調用時,表示已完成下載
 */
- (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];
            }
        }
    }
}
複製代碼

由此,能夠看出AFURLSessionManagerTaskDelegate這個類主要是用來監控上傳與下載的進度,以及對task完成後數據的處理和回調。json

2.6._AFURLSessionTaskSwizzling私有類

2.6.1.靜態方法

/**
 交換某個類兩個方法的實現
 */
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);
}

/**
 給一個類添加一個方法
 */
static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
    return class_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));
}
複製代碼

2.6.2.靜態常量

/**
 當一個task從新開始時就會發送這個通知
 */
static NSString * const AFNSURLSessionTaskDidResumeNotification  = @"com.alamofire.networking.nsurlsessiontask.resume";

/**
 當一個task暫停時就會發送這個通知
 */
static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend";
複製代碼

2.6.3.方法實現

+ (void)load {
    /**
     WARNING: Trouble Ahead
     https://github.com/AFNetworking/AFNetworking/pull/2702
     */

    // 判斷是否有NSURLSessionTask這個類
    if (NSClassFromString(@"NSURLSessionTask")) {
        /**
         NSURLSessionTask的實如今iOS 7和iOS 8中有所不一樣,這使得下面代碼實現起來有些棘手。
         已經作了許多單元測試來驗證這個方法的可行性。
         目前咱們所知道的是:
            - NSURLSessionTasks是經過類簇設計模式實現的,這就意味着你經過這個類提供的接口得到的類並非這個類。
            - 簡單的經過 ‘[NSURLSessionTask class]’ 方法並不起做用,你須要經過NSURLSession建立一個task,才能獲取它所在的類。
            - 在iOS 7中,‘localDataTask’是的類型是‘__NSCFLocalDataTask’,它的繼承關係是:__NSCFLocalDataTask -> __NSCFLocalSessionTask -> __NSCFURLSessionTask。
            - 在iOS 8中,‘localDataTask’是的類型是‘__NSCFLocalDataTask’,它的繼承關係是:__NSCFLocalDataTask -> __NSCFLocalSessionTask -> NSURLSessionTask。
            - 在iOS 7中,只有‘__NSCFLocalSessionTask’和其父類‘__NSCFURLSessionTask’這兩個類實現了它們的‘resume’和‘suspend’方法,而且在方法實現裏‘__NSCFLocalSessionTask’類並無調用其父類的方法,這就意味着兩個類都要進行方法交換。
            - 在iOS 8中,只有‘NSURLSessionTask’類實現了‘resume’和‘suspend’方法,這就意味着只對該類進行方法交換便可。
            - 由於‘NSURLSessionTask’類並非在每一個iOS的版本中都存在,全部在這個虛構類裏能夠更容易添加和管理交換方法
        
         一些假設前提:
            - ‘resume’和‘suspend’方法在實現時,並無調用其父類的實現方法。但若是在之後的iOS版本中調用了其父類的實現方法,咱們還要再進行處理。
            - ‘resume’和‘suspend’方法不會被其餘類複寫
         
         目前的解決方案:
            1) 先經過‘NSURLSession’實例化一個dataTask對象,再經過dataTask對象獲取‘__NSCFLocalDataTask’對象。
            2) 獲取指向‘af_resume’方法原始實現的指針。
            3) 檢查當前類是否實現了resume方法,若是實現了就繼續執行第4步
            4) 獲取當前類的父類
            5) 獲取當前類指向‘resume’方法實現的指針。
            6) 獲取當前類的父類指向‘resume’方法實現的指針。
            7) 若是當前類和其父類指向‘resume’方法實現的指針不一致,而且當前類指向‘resume’方法實現的指針和指向‘af_resume’方法原始實現的指針也不一致,就進行方法交換
            8) 而後再經過步驟3-8檢查其父類
         */
        // 實例化網絡會話配置對象
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        // 實例化網絡會話對象
        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
        // 實例化網絡會話任務對象
        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
        // 獲取指向當前類的af_resume方法原始實現的指針
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        // 獲取網絡會話任務對象的具體類
        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)));
            // 獲取指向這個具體類的父類的resume方法實現的指針
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
            // 若是指向這個具體類的resume方法實現的指針,和指向其的父類的resume方法實現的指針,以及指向當前類的af_resume方法原始實現的指針,都不一致
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                // 向當前這個具體類添加af_resume和af_suspend方法,並與其resume和suspend方法做交換
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
            // 獲取當前這個具體類的父類,繼續循環判斷其父類
            currentClass = [currentClass superclass];
        }
        
        // 取消這個網絡會話任務
        [localDataTask cancel];
        // 當即結束這個網絡會話
        [session finishTasksAndInvalidate];
    }
}

/**
 爲一個了動態添加af_resume和af_suspend方法,並與之對應的resume和suspend作交換
 */
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
    // 獲取本類的af_resume和af_suspend方法
    Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
    Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));

    // 若是想目標類中成功添加了af_resume方法
    if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
        // 交換目標類的resume和af_resume方法
        af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
    }

    // 若是想目標類中成功添加了af_suspend方法
    if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
        // 交換目標類的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;
}

- (void)af_resume {
    // 獲取NSURLSessionDataTask對象的state屬性,並調用其resume方法
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];
    
    // 若是在調用重啓方法以前的狀態不是正常運行狀態,就發送通知
    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}

- (void)af_suspend {
    // 獲取NSURLSessionDataTask對象的state屬性,並調用其suspend方法
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_suspend];
    
    // 若是在調用暫停方法以前的狀態不是暫停狀態,就發送通知
    if (state != NSURLSessionTaskStateSuspended) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}
複製代碼

讀完這個私有類後,發現一個問題:就是_AFURLSessionTaskSwizzling並無被調用,甚至沒有出如今其餘代碼的任何一個地方,這就很奇怪。既然沒有調用,寫了這麼一大堆代碼是怎麼起做用的呢?通過查閱資料發現:當類被引用進項目的時候就會執行load函數(在main函數開始執行以前),與這個類是否被用到無關,每一個類的load函數只會自動調用一次。要是想要進一步瞭解有關load方法的話,能夠看這篇文章:iOS類方法load和initialize詳解,若是想要深刻了解的話能夠看這篇文章:你真的瞭解load方法麼?設計模式

2.7.類擴展

/**
 保存網絡會話配置對象
 */
@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;

/**
 保存回調操做隊列對象
 */
@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;

/**
 保存網絡會話對象
 */
@property (readwrite, nonatomic, strong) NSURLSession *session;

/**
 保存AFURLSessionManagerTaskDelegate對象和task之間的聯繫
 */
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;

/**
 保存task的taskDescription屬性值,在代碼中被設置成當前對象的地址值
 */
@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;

/**
 保存鎖對象,這個鎖用來保證對mutableTaskDelegatesKeyedByTaskIdentifier存取的線程安全
 */
@property (readwrite, nonatomic, strong) NSLock *lock;

/**
 保存session無效時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;

/**
 保存session接收到驗證請求時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;

/**
 保存session隊列中全部的消息都發送出去時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;

/**
 保存一個HTTP請求試圖執行重定向到一個不一樣的URL時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;

/**
 保存task接收到身份驗證時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;

/**
 保存task須要一個新的輸入流時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;

/**
 保存一個來按期跟蹤上傳進度的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;

/**
 保存task執行完成時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;

/**
 保存dataTask接收到響應時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;

/**
 保存dataTask變成downloadTask時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;

/**
 保存dataTask接收到數據時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;

/**
 保存dataTask將要對響應進行緩存時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;

/**
 保存downloadTask完成一個下載時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;

/**
 保存一個來按期跟蹤下載進度的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;

/**
 保存downloadTask從新開始下載時的block回調
 */
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
複製代碼

2.8.方法實現

  • 生命週期方法
- (instancetype)init {
    // 默認配置初始化
    return [self initWithSessionConfiguration:nil];
}

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    // 若是沒有傳入配置對象就使用默認網絡會話配置
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    self.sessionConfiguration = configuration;

    // 實例化一個操做隊列並設置最大併發數爲1
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    // 經過上面生成的兩個對象實例化網絡會話對象
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

    // 默認響應序列化對象是序列化json數據對象
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    // 默認的安全策略是默認安全策略
    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,並進行置空處理,主要是爲了從後臺切換到前臺時從新初始化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];
}
複製代碼
  • 懶加載方法
/**
 獲取當前AFURLSessionManager對象的地址字符串賦值給task的taskDescription屬性,目的是經過這個字符串來判斷通知監聽到的是不是當前AFURLSessionManager對象所擁有的task發出的
 */
- (NSString *)taskDescriptionForSessionTasks {
    return [NSString stringWithFormat:@"%p", self];
}
複製代碼
  • 通知響應方法
/**
 當task已經重啓的時候會經過通知調用這個方法
 */
- (void)taskDidResume:(NSNotification *)notification {
    // 從通知對象中獲取到發送這條通知的task
    NSURLSessionTask *task = notification.object;
    // 若是這個task屬於這個AFURLSessionManager對象
    if ([task respondsToSelector:@selector(taskDescription)]) {
        if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
            // 主線程異步發送通知task已經重啓
            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
            });
        }
    }
}

/**
 當task已經暫停的時候會經過通知調用這個方法
 */
- (void)taskDidSuspend:(NSNotification *)notification {
    // 從通知對象中獲取到發送通知的task
    NSURLSessionTask *task = notification.object;
    // 若是這個task屬於這個AFURLSessionManager對象
    if ([task respondsToSelector:@selector(taskDescription)]) {
        if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                // 主線程異步發送通知task已經暫停
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
            });
        }
    }
}
複製代碼
  • 處理AFURLSessionManagerTaskDelegate對象和NSURLSessionTask對象關係的私有方法
/**
 經過task對象獲取其綁定的AFURLSessionManagerTaskDelegate對象
 */
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
    // 在debug下,缺乏task參數就crash
    NSParameterAssert(task);

    // 在線程安全的環境下,經過task的taskIdentifier屬性,從mutableTaskDelegatesKeyedByTaskIdentifier屬性中獲取綁定的delegate
    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
{
    // 在debug下,缺乏參數就crash
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    // 在線程安全的環境下
    [self.lock lock];
    // 以task的taskIdentifier屬性爲key,以delegate爲value,保存到mutableTaskDelegatesKeyedByTaskIdentifier屬性中。
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    // 添加通知監聽task的重啓和暫停事件
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

/**
 爲dataTask綁定delegate
 */
- (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
{
    // 經過dataTask實例化delegate對象,並初始化屬性
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    // 標記dataTask所屬的AFURLSessionManager對象
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    
    // 爲dataTask綁定delegate
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

/**
 爲uploadTask綁定delegate
 */
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
                        progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
               completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    // 經過uploadTask實例化delegate對象,並初始化屬性
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    // 標記uploadTask所屬的AFURLSessionManager對象
    uploadTask.taskDescription = self.taskDescriptionForSessionTasks;

    // 爲uploadTask綁定delegate
    [self setDelegate:delegate forTask:uploadTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
}

/**
 爲downloadTask綁定delegate
 */
- (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
{
    // 經過downloadTask實例化delegate對象,並初始化屬性
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    // 若是設置了保存下載文件的路徑,就賦值給delegate
    if (destination) {
        delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
            return destination(location, task.response);
        };
    }

    // 標記downloadTask所屬的AFURLSessionManager對象
    downloadTask.taskDescription = self.taskDescriptionForSessionTasks;

    // 爲downloadTask綁定delegate
    [self setDelegate:delegate forTask:downloadTask];

    delegate.downloadProgressBlock = downloadProgressBlock;
}

/**
 移除task綁定的delegate
 */
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
    // 在debug下,缺乏參數就crash
    NSParameterAssert(task);

    // 在線程安全的環境下
    [self.lock lock];
    // 移除對task觀察的通知
    [self removeNotificationObserverForTask:task];
    // 從mutableTaskDelegatesKeyedByTaskIdentifier移除task
    [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
    [self.lock unlock];
}
複製代碼
  • 公共接口獲取task相關屬性的get方法
/**
 經過傳入的不一樣類型task的名稱獲取相應的tasks
 */
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    // 建立臨時變量保存獲取到的tasks
    __block NSArray *tasks = nil;
    // 建立信號量,由於getTasksWithCompletionHandler這個方法是異步獲取tasks,爲了不還沒獲取到tasks就返回,就使用信號量進行控制
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        // 根據傳入的不一樣類型task的名稱獲取相應的tasks
        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))]) {
            // 利用valueForKeyPath合併數組並保留重複值
            tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
        }

        // 發送一個信號量,使semaphore變爲1,表示已經獲取到tasks,能夠向下執行
        dispatch_semaphore_signal(semaphore);
    }];

    // 等待信號量變爲1
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return tasks;
}

/**
 獲取當前AFURLSessionManager對象的全部tasks
 */
- (NSArray *)tasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}

/**
 獲取當前AFURLSessionManager對象的全部dataTasks
 */
- (NSArray *)dataTasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}

/**
 獲取當前AFURLSessionManager對象的全部uploadTasks 
 */
- (NSArray *)uploadTasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}

/**
 獲取當前AFURLSessionManager對象的全部downloadTasks
 */
- (NSArray *)downloadTasks {
    return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
複製代碼

經過tasksForKeyPath:方法的實現,咱們能夠學到如何利用GCD的信號量將異步回調轉換爲同步執行數組

  • 公共接口結束網絡會話方法
/**
 當傳YES,就當即關閉當前網絡會話;當傳NO,等待當前任務完成後再關閉當前對話
 */
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks {
    if (cancelPendingTasks) {
        [self.session invalidateAndCancel];
    } else {
        [self.session finishTasksAndInvalidate];
    }
}
複製代碼
  • 公共接口設置responseSerializer相關屬性的set方法
- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer {
    // 在debug下,缺乏參數就crash
    NSParameterAssert(responseSerializer);

    // 保存設置的參數
    _responseSerializer = responseSerializer;
}
複製代碼
  • 處理task和其通知的私有方法
/**
 向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];
}

/**
 移除task的觀察通知
 */
- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task];
}
複製代碼

在以前看過的那個私有類_AFURLSessionTaskSwizzling中,AFNetworking交換了taskresumesuspend方法。緩存

resume方法爲例說明其內部實現,當調用taskresume方法時,實際上調用的是af_resume方法。安全

af_resume方法中,又調用了af_resume方法,實際上調用的是系統的resume方法,同時發送了通知AFNSURLSessionTaskDidResumeNotificationbash

當前AFURLSessionManager對象經過觀察接收到通知後,就調用taskDidResume:方法。

taskDidResume:方法中判斷觸發的resume方法是當前AFURLSessionManager對象所持有的task後,在主線程異步發送AFNetworkingTaskDidResumeNotification通知,告訴外界這個task重啓了。

經過這一系列的處理,能夠看出AFNetworking想要實現的,其實就是想在原來方法的功能上,添加向外發送通知功能,已達到監聽的目的。只不過因爲蘋果在不一樣系統版本上對NSURLSessionTask這個類的內部實現不一樣,以及類簇的設計模式,致使了實現起來「a bit tricky」

  • 建立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 *)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 {

    // 安全的經過傳入的request建立一個dataTask
    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    // 爲dataTask綁定AFURLSessionManagerTaskDelegate對象,以監聽dataTask的處理進度和處理數據
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}
複製代碼
  • 建立NSURLSessionUploadTask對象的公共方法
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    // 安全的經過傳入的request和fileURL建立一個uploadTask
    __block NSURLSessionUploadTask *uploadTask = nil;
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
        
        // 在iOS7上可能會建立失敗
        // uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113)
        // 若是沒有建立成功,且容許從新建立,且NSURLSessionConfiguration對象是經過backgroundSessionConfigurationWithIdentifier:方法建立的,就重複嘗試三次
        if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
            for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
                uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
            }
        }
    });
    
    // 爲uploadTask綁定AFURLSessionManagerTaskDelegate對象,以監聽uploadTask的處理進度和處理數據
    if (uploadTask) {
        [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
{
    // 安全的經過傳入的request和bodyData建立一個uploadTask
    __block NSURLSessionUploadTask *uploadTask = nil;
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
    });

    // 爲uploadTask綁定AFURLSessionManagerTaskDelegate對象,以監聽uploadTask的處理進度和處理數據
    [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
{
    // 安全的經過傳入的request建立一個uploadTask
    __block NSURLSessionUploadTask *uploadTask = nil;
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithStreamedRequest:request];
    });

    // 爲uploadTask綁定AFURLSessionManagerTaskDelegate對象,以監聽uploadTask的處理進度和處理數據
    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];

    return uploadTask;
}
複製代碼
  • 建立NSURLSessionDownloadTask對象的公共方法
- (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
{
    // 安全的經過傳入的request建立一個downloadTask
    __block NSURLSessionDownloadTask *downloadTask = nil;
    url_session_manager_create_task_safely(^{
        downloadTask = [self.session downloadTaskWithRequest:request];
    });

    // 爲downloadTask綁定AFURLSessionManagerTaskDelegate對象,以監聽downloadTask的處理進度和處理數據
    [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
{
    // 安全的經過傳入的resumeData重啓一個downloadTask
    __block NSURLSessionDownloadTask *downloadTask = nil;
    url_session_manager_create_task_safely(^{
        downloadTask = [self.session downloadTaskWithResumeData:resumeData];
    });

    // 爲downloadTask綁定AFURLSessionManagerTaskDelegate對象,以監聽downloadTask的處理進度和處理數據
    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];

    return downloadTask;
}
複製代碼
  • 獲取tasks進度方法的公共方法
- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
    return [[self delegateForTask:task] uploadProgress];
}

- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
    return [[self delegateForTask:task] downloadProgress];
}
複製代碼

這兩個方法都是先經過傳入的task參數獲取到與之綁定的AFURLSessionManagerTaskDelegate對象,再經過AFURLSessionManagerTaskDelegate對象獲取其監聽的進度數據

  • 設置NSURLSessionDelegate回調的公共方法
- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
    self.sessionDidBecomeInvalid = block;
}

- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
    self.sessionDidReceiveAuthenticationChallenge = block;
}
複製代碼
  • 設置NSURLSessionTaskDelegate回調的公共方法
- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block {
    self.taskNeedNewBodyStream = block;
}

- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block {
    self.taskWillPerformHTTPRedirection = block;
}

- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
    self.taskDidReceiveAuthenticationChallenge = block;
}

- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
    self.taskDidSendBodyData = block;
}

- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block {
    self.taskDidComplete = block;
}
複製代碼
  • 設置NSURLSessionDataDelegate回調的公共方法
- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block {
    self.dataTaskDidReceiveResponse = block;
}

- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block {
    self.dataTaskDidBecomeDownloadTask = block;
}

- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block {
    self.dataTaskDidReceiveData = block;
}

- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block {
    self.dataTaskWillCacheResponse = block;
}

- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block {
    self.didFinishEventsForBackgroundURLSession = block;
}
複製代碼
  • 設置NSURLSessionDownloadDelegate回調的公共方法
- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block {
    self.downloadTaskDidFinishDownloading = block;
}

- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block {
    self.downloadTaskDidWriteData = block;
}

- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block {
    self.downloadTaskDidResume = block;
}
複製代碼
  • NSObject方法
- (NSString *)description {
    // 定製打印數據
    return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue];
}

- (BOOL)respondsToSelector:(SEL)selector {
    // 若是沒有對相應的block進行賦值,也不會去調用實現相應的代理
    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方法實現
/**
 當session無效時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
    // 調用block回調數據
    if (self.sessionDidBecomeInvalid) {
        self.sessionDidBecomeInvalid(session, error);
    }

    // 發送通知
    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}

/**
 當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
        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;
            }
        // 若是驗證方式不是NSURLAuthenticationMethodServerTrust,也使用默認方式驗證證書
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }

    // 回調結果
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}
複製代碼
  • NSURLSessionTaskDelegate方法實現
/**
 當一個HTTP請求試圖執行重定向到一個不一樣的URL時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest *))completionHandler
{
    NSURLRequest *redirectRequest = request;

    // 若是經過block傳入重定向的處理方式,就傳參調用
    if (self.taskWillPerformHTTPRedirection) {
        redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }

    // 回調結果
    if (completionHandler) {
        completionHandler(redirectRequest);
    }
}

/**
 當task接收到身份驗證時,會調用這個代理方法。這個證書驗證的處理方式和上面那個如出一轍,只不過上面那個是session級別的,這個是task級別的
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    // 設置臨時變量保存數據
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    __block NSURLCredential *credential = nil;

    // 若是經過block傳入了證書處理方式,則直接傳參調用
    if (self.taskDidReceiveAuthenticationChallenge) {
        disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
    // 若是沒有傳入證書處理方式
    } else {
        // 若是驗證方式爲NSURLAuthenticationMethodServerTrust
        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;
            }
        // 若是驗證方式不是NSURLAuthenticationMethodServerTrust,就使用默認方式驗證證書
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    }

    // 回調結果
    if (completionHandler) {
        completionHandler(disposition, credential);
    }
}

/**
 當task須要一個新的輸入流時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
 needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
    // 建立臨時變量保存數據
    NSInputStream *inputStream = nil;

    // 若是經過block回調傳入獲取輸入流的方式,就傳參調用
    if (self.taskNeedNewBodyStream) {
        inputStream = self.taskNeedNewBodyStream(session, task);
    // 不然若是task中原有輸入流,就用原有輸入流
    } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
        inputStream = [task.originalRequest.HTTPBodyStream copy];
    }

    // 回調結果
    if (completionHandler) {
        completionHandler(inputStream);
    }
}

/**
 當task已經發送出數據時,會調用這個代理方法
 */
- (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) {
        // 經過請求頭中的「Content-Length」字段獲取大小
        NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
        if(contentLength) {
            totalUnitCount = (int64_t) [contentLength longLongValue];
        }
    }
    
    // 獲取與task綁定的delegate對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
    // 調用delegate對象所實現相同的代理方法來監聽進度
    if (delegate) {
        [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
    }

    // 調用block回調數據
    if (self.taskDidSendBodyData) {
        self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
    }
}

/**
 當task執行完成時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    // 獲取與task綁定的delegate對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // 加判斷的緣由是,在後臺結束這個task時,其delegate可能會被置nil
    // delegate may be nil when completing a task in the background
    if (delegate) {
        // 調用delegate對象所實現相同的代理方法來處理結果
        [delegate URLSession:session task:task didCompleteWithError:error];

        // 移除對task監聽的通知和其綁定的delegate
        [self removeDelegateForTask:task];
    }

    // 調用block回調數據
    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}
複製代碼
  • NSURLSessionDataDelegate方法實現
/**
 當dataTask接收到響應時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
    // 建立臨時變量保存接收到數據後的處理模式
    NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;

    // 若是經過block傳入了處理方式,則傳參調用
    if (self.dataTaskDidReceiveResponse) {
        disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
    }

    // 回調結果
    if (completionHandler) {
        completionHandler(disposition);
    }
}

/**
 當dataTask變成downloadTask時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
    // 獲取與dataTask綁定的delegate對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    if (delegate) {
        // 把delegate從dataTask上解綁,而後綁定到downloadTask
        [self removeDelegateForTask:dataTask];
        [self setDelegate:delegate forTask:downloadTask];
    }

    // 調用block回調數據
    if (self.dataTaskDidBecomeDownloadTask) {
        self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
    }
}

/**
 當dataTask接收到數據時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{

    // 獲取與dataTask綁定的delegate對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    // 調用delegate對象所實現相同的代理方法來處理結果
    [delegate URLSession:session dataTask:dataTask didReceiveData:data];

    // 調用block回調數據
    if (self.dataTaskDidReceiveData) {
        self.dataTaskDidReceiveData(session, dataTask, data);
    }
}

/**
 當dataTask將要對響應進行緩存時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
{
    // 建立臨時變量保存要緩存的相應對象
    NSCachedURLResponse *cachedResponse = proposedResponse;

    // 若是經過block傳入了處理方式,則傳參調用
    if (self.dataTaskWillCacheResponse) {
        cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
    }

    // 回調結果
    if (completionHandler) {
        completionHandler(cachedResponse);
    }
}

/**
 當session隊列中全部的消息都發送出去時,會調用這個代理方法
 */
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    // 主隊列異步回調結果
    if (self.didFinishEventsForBackgroundURLSession) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.didFinishEventsForBackgroundURLSession(session);
        });
    }
}
複製代碼
  • NSURLSessionDownloadDelegate方法實現
/**
 當downloadTask完成一個下載時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    // 獲取與downloadTask綁定的delegate對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
    // 若是經過block傳入了處理方式
    if (self.downloadTaskDidFinishDownloading) {
        // 則傳參調用獲取下載文件保存路徑
        NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
        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;
        }
    }

    // 調用delegate對象所實現相同的代理方法來處理結果
    if (delegate) {
        [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
    }
}

/**
 downloadTask下載時的按期回調代理
 */
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    
    // 獲取與downloadTask綁定的delegate對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
    
    // 調用delegate對象所實現相同的代理方法來處理數據
    if (delegate) {
        [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
    }

    // 調用block回調數據
    if (self.downloadTaskDidWriteData) {
        self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    }
}

/**
 當downloadTask從新開始下載時,會調用這個代理方法
 */
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
    
    // 獲取與downloadTask綁定的delegate對象
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
    
    // 調用delegate對象所實現相同的代理方法來處理數據
    if (delegate) {
        [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
    }

    // 調用block回調數據
    if (self.downloadTaskDidResume) {
        self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
    }
}
複製代碼
  • NSSecureCoding方法實現
+ (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;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
}
複製代碼

關於NSSecureCoding代理方法的實如今AFNetworking源碼閱讀(二)——從AFURLRequestSerialization入手這篇文章中的4.1.2.4.7 NSSecureCoding協議方法的實現段落已經介紹過。

  • NSCopying方法實現
- (instancetype)copyWithZone:(NSZone *)zone {
    return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];
}
複製代碼

3.總結

到這裏,AFURLSessionManager這個類咱們就看完了。能夠看出該類是負責管理NSURLSession對象,建立各類類型的task,並監聽其屬性的狀態,處理代理和返回的數據。

源碼閱讀系列:AFNetworking

源碼閱讀:AFNetworking(一)——從使用入手

源碼閱讀:AFNetworking(二)——AFURLRequestSerialization

源碼閱讀:AFNetworking(三)——AFURLResponseSerialization

源碼閱讀:AFNetworking(四)——AFSecurityPolicy

源碼閱讀:AFNetworking(五)——AFNetworkReachabilityManager

源碼閱讀:AFNetworking(六)——AFURLSessionManager

源碼閱讀:AFNetworking(七)——AFHTTPSessionManager

源碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache

源碼閱讀:AFNetworking(九)——AFImageDownloader

源碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager

源碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking

源碼閱讀:AFNetworking(十二)——UIButton+AFNetworking

源碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking

源碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking

源碼閱讀:AFNetworking(十五)——UIRefreshControl+AFNetworking

源碼閱讀:AFNetworking(十六)——UIWebView+AFNetworking

相關文章
相關標籤/搜索