原始碼閱讀:AFNetworking(六)——AFURLSessionManager

堯少羽發表於2018-03-05

該文章閱讀的AFNetworking的版本為3.2.0。

完成了前面對AFURLRequestSerializationAFURLResponseSerializationAFSecurityPolicyAFNetworkReachabilityManager類的閱讀,接下來就可以閱讀AFNetworking的核心類AFURLSessionManager啦。通過一開始對標頭檔案的引用,就可以知道AFURLSessionManager類的核心地位:

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

之前對上述類的閱讀,就是為了給本類閱讀做鋪墊。

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.0

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完成後資料的處理和回撥。

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方法,同時傳送了通知AFNSURLSessionTaskDidResumeNotification

當前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

相關文章