前言
在AFNetworking源碼閱讀1中,我們已經閱讀完了AFHTTPSessionManager
類。本篇我們閱讀其父類AFURLSessionManager
。該類是整個框架的核心。
源碼:AFURLSessionManager
先來看看頭文件AFURLSessionManager.h
該類代碼太長,我們從頭至尾一段一段看。先看頭部的屬性部分:
@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
// The managed session.
@property (readonly, nonatomic, strong) NSURLSession *session;
// The operation queue on which delegate callbacks are run.
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; // 代理回調所運行的操作隊列
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; // 安全策略
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
// The data, upload, and download tasks currently run by the managed session.
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
// The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
// The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
第一個屬性session
就是該類所管理的session;operationQueue
是代理回調所運行的操作隊列;responseSerializer
是解析網絡返回數據的序列化器,它是實現了AFURLResponseSerialization
協議的任意類型;securityPolicy
有關網絡安全連接的安全策略,這個后面還要繼續研究;reachabilityManager
是檢測網絡狀態的檢測器,后面也會繼續研究;tasks
代表管理的session此時正在運行的data task,upload task,download task們;completionQueue
為completionBlock
的dispatch queue,而completionGroup
為completionBlock
的dispatch group。
屬性看完,該輪到初始化方法了:
#pragma mark - init method
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks; // 選擇性地取消掛起的task
可以看到,第一個方法為初始化方法,第二個方法為選擇性的取消掛起的session task,只需要傳入某個掛起的task為參數,便可取消該task。
這里需要多說一句的是初始化方法后面有個標識符:NS_DESIGNATED_INITIALIZER
,它代表什么意思呢?它呀,意為“被設計的初始化器”,“被指定的初始化方法”。
詳情請查看:iOS: 聊聊 Designated Initializer(指定初始化函數)
正確使用NS_DESIGNATED_INITIALIZER
接下來到了核心方法部分了:
#pragma mark - create task from request
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE;
- (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,從而創建uploadTask
- (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;
可以看到以session task類型的不同,提供了不同的方法,并且同一類型的session task下也可能提供多個不同場景的方法。(篇幅有限,部分方法省略)
多說一句:DEPRECATED_ATTRIBUTE
標識符的意思是慢慢棄用的屬性或接口,如果我們使用了,在xcode中就會出現警告??信息。
接下來是這兩個方法:
#pragma mark - get progress for task
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
這兩個方法看名字就可以明白其意義:獲得某個task的進度。
再接著就是session delegate callback的setter方法了,它們是和session以及session task的代理方法是一一對應的。在這里不作說明,等到閱讀到代理方法時就明白了。
接下來就是最后一部分代碼:
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification; // Posted when a task resumes.
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification; // Posted when a task finishes executing
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification; // Posted when a task suspends its execution.
FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification; // Posted when a session is invalidated.
FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification; // Posted when a session download task encountered an error when moving the temporary download file to a specified destination.
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey;
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey;
上面的代碼,聲明了一些用于通知的全局常量。說起全局常量的使用方法:先在.h文件中用** extern聲明此全局常量,然后在.m文件中定義該全局常量。《52個有效方法》筆記1——熟悉Objective-C。但在這里聲明時并沒有用extern**,而是用了
FOUNDATION_EXPORT
,關于它的解釋見:FOUNDATION_EXPORT 或#define 或 extern
現在開始看AFURLSessionManager.m
:
代碼太長太長,我們一段一段來分析。先來看看Extension部分:
@interface AFURLSessionManager ()
@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
@property (readwrite, nonatomic, strong) NSURLSession *session;
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
@property (readwrite, nonatomic, strong) NSLock *lock;
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
@end
這里又定義了一個readwrite的session
作為私有屬性。而在頭文件中定義的session
屬性是readonly
的。這么做更安全,在內部我們可對該屬性進行讀寫操作,而暴露給外部時,使用者只能read。
mutableTaskDelegatesKeyedByTaskIdentifier
屬性是個可變字典。該屬性有大用處,以taskIdentifier作為key,以task delegate為value,將task和其delegate一對一綁定,后面還需細說。
然后后面均為回調block的屬性。
然后我們看看初始化方法:
- (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;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer]; // 默認的反序列化類型是AFJSONResponseSerializer
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 遍歷所管理的session的task,給每個task設置一個delegate
[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];
}
在初始化方法中可以看到。首先,若初始化時傳入的參數configuration
為nil,則默認初始化為defaultSessionConfiguration
;然后就是初始化各個屬性了,session
屬性是通過NSURLSession
的類方法sessionWithConfiguration:delegate:delegateQueue:
方法來初始化的,設delegate為self。當時閱讀代碼時,始終有個疑問:本類實現了四個協議,第一個為session的協議,是在這里設置delegate為self了。后面三個協議我始終找不到在哪設置session task的delegate為self的地方,覺得很疑惑,這沒地方設置過代理,為什么實現了代理方法?代理方法怎么會執行呢?后面我從網上才看到,只要設置了session的delegate,那也等同于設置該session創建的session task的代理。還因這個問題找了半天代碼...
初始化方法的最后遍歷了所管理的session的task,給每個task設置了一個delegate。關于設置delegate這個方法的實現細節,我們暫且等到下文詳解。但這里我也有個疑惑:這里是初始化方法,session
才剛剛被初始化,該session應當還沒有對應的session task啊,為何要遍歷其session task?會有嗎?這個問題百思不得其解,希望哪位大兄弟看到能解惑...
然后我們看這個類最重要的方法:
- (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 {
// 1.調用NSURLSession的實例方法dataTaskWithRequest生成dataTask
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
// 2.為該dataTask設置delegate,將dataTask與delegate一一綁定
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
看到這個方法名,我們大概能猜到該方法內部的實現步驟:因為參數為request,返回值時dataTask,那很可能是完成了由request生成dataTask的動作。但是應該還不僅僅是這樣,因為該方法還有block回調,block回調的內容是怎么來的呢?
好了,一看代碼果然和我們猜的差不多。這里面主要有兩個步驟:
1.調用NSURLSession的實例方法dataTaskWithRequest:
生成dataTask。
url_session_manager_create_task_safely
是為了修復某個bug,我們在此不必糾結。
2.為該dataTask設置delegate,將dataTask與delegate一一綁定。并且通過block傳遞出來該請求任務的一些進度、數據、錯誤信息等。
我們看看第二個步驟的方法實現:
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
/*
創建一個AFURLSessionManagerTaskDelegate代理類的對象,并為幾個屬性賦值。然后調用setDelegate:forTask:將其和dataTask綁定
*/
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask]; // 將delegate和task綁定的核心
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
可以看到,里面創建了一個AFURLSessionManagerTaskDelegate
代理類的對象,并為幾個屬性賦值(主要為block回調屬性)。然后調用setDelegate:forTask:
將此delegate
實例對象和該task
綁定。但實現此功能的是setDelegate:forTask:
方法。
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
// 將task和代理類綁定,task的taskIdentifier作為字典的key,delegate作為字典的value
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task]; // 該方法主要是設置兩個NSProgress類型的變量uploadProgress和downloadProgress的屬性
[self addNotificationObserverForTask:task]; // 給該task添加兩個KVO事件
[self.lock unlock];
}
該方法內部有三個步驟:
1.將task的taskIdentifier作為鍵,將delegate作為值,賦給可變字典mutableTaskDelegatesKeyedByTaskIdentifier
。完成delegate和task的一對一綁定;
2.調用delegate的setupProgressForTask:
方法。由task設置delegate對象的uploadProgress
和downloadProgress
屬性的屬性值。
3.給該task添加兩個觀察(啟動和暫停)。
關于步驟一,相當于對mutableTaskDelegatesKeyedByTaskIdentifier
可變字典的set操作,當然還有與此對應的get操作,獲得某task對應的delegate。
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
該類中有多處用到了get操作,比如:
移除某個task對應的delegate。首先通過delegateForTask:
方法獲得此delegate,然后移除此task的進度觀察,移除啟動和暫停的觀察,并最終從字典中刪除此task。
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[self.lock lock];
[delegate cleanUpProgressForTask:task];
[self removeNotificationObserverForTask:task];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
又比如:
** 獲得該task的上傳或下載進度。**
- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
return [[self delegateForTask:task] uploadProgress];
}
- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
return [[self delegateForTask:task] downloadProgress];
}
再比如,我們待會會講到的,在該類實現的代理方法里,有時還需要調用在其代理類AFURLSessionManagerTaskDelegate
中實現的代理方法。此時就需要調用delegateForTask:
方法得到相應的delegate。
關于步驟三的實現詳情:
- (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添加啟動和暫停的通知,當該task啟動時就執行taskDidResume:
方法,當該task暫停時就執行taskDidSuspend:
方法。
- (NSString *)taskDescriptionForSessionTasks {
return [NSString stringWithFormat:@"%p", self];
}
- (void)taskDidResume:(NSNotification *)notification {
NSURLSessionTask *task = notification.object;
if ([task respondsToSelector:@selector(taskDescription)]) {
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
});
}
}
}
taskDidResume:
和taskDidSuspend:
兩個方法的代碼是什么意思,沒明白...不應該是在某個地方發出了通知,然后在這觀察到了,然后就會執行這兩個方法嗎?那為什么,在這倆方法里還post notification???
更新:taskDidResume:
和taskDidSuspend:
分別是AFNSURLSessionTaskDidSuspendNotification
和AFNSURLSessionTaskDidSuspendNotification
的回調方法,我們上面說其意分別代表為task啟動和暫停。為了確認,我們在代碼中找找post出這倆通知的地方。
- (void)af_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 {
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];
}
}
af_resume
和af_suspend
是什么方法呢?我們預期它應該是在該task執行resume
和suspend
方法后便立即post通知的。我們尋根溯源,繼續找af_resume
和af_suspend
倆方法是在哪觸發的:
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
}
if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
}
}
可以看到那倆方法大量基本出現在swizzleResumeAndSuspendMethodForClass:
這個方法中,繼續找尋根溯源,原來這個方法是在_AFURLSessionTaskSwizzling
這個類的load
方法中調用的。_AFURLSessionTaskSwizzling
這個類干了什么,我們研究研究。
說到這我們得說說
Method Swizzing
,我在這篇文中已經說過:《52個有效方法》筆記3——探究runtime摘錄如下:
OC對象都有一個isa指針,該指針指向一個方法調度表,它存儲著該對象的方法。并且,它的形式是鍵值對,key為SEL,value為IMP。
既然如此,那我們可以在SEL和IMP的映射上做文章。也就是說我可以人為地改變SEL和IMP之間的映射關系。
這樣做有什么意義嗎?嗯,意義很大。我們可以把原有的類和我們自定義類的映射對調,那這樣,即使我們在代碼里調用的是原有類的方法,但暗地里卻神不知鬼不覺地在執行我們自定義的方法。哈哈,說得有些玄乎了。
其實,在實踐中Method Swizzing可以完成AOP或者對原有類的拓展。
在本例中,就將原始的resume
和suspend
倆方法的實現IMP
調包了,調成了_AFURLSessionTaskSwizzling
這個類自己實現的af_resume
和af_suspend
。而在af_resume
和af_suspend
倆方法里,我們分別post出了相應的通知。如以一來,即便是程序是在調用原始的resume
方法,但程序實際上卻神不知鬼不覺地執行了af_resume
,發送了通知出來。
另外之前看這塊代碼時覺得蒙圈的一個地方是把````taskDidResume:和
taskDidSuspend:倆方法里發送的通知名字看錯了??。在這倆回調方法里post出的是
AFNetworkingTaskDidResumeNotification和
AFNetworkingTaskDidSuspendNotification通知,主要是用于通知UI層的。并不是
AFNSURLSessionTaskDidResumeNotification和
AFNSURLSessionTaskDidSuspendNotification``通知,當時太蒙圈了,怎么在觀察到通知的回調里再次發出通知??。
現在,我們來看看在該類中實現的代理方法。在該類中實現了4個協議的代理方法。
有關這些代理方法的含義及具體實現的講解這篇文章講解的很詳細,在此我不再寫了:
AFNetworking源碼閱讀(三)
但是還是有幾個點需要說明一下。
首先,就是我們可以看到在這些代理方法的實現里,很多都會先判斷有無與代理方法對應的自定義的block回調(這個我們在前面講頭文件時提到過,那些block都是與代理方法對應的)。若有,則調用自定義的block,在定義的block回調里做相應的處理。
說起自定義的block回調。若按我平時的寫法習慣,我一般都是直接在頭文件中定義為公開屬性。這樣在.m文件里直接調用就行。但是這里的源碼卻不是,它沒有在頭文件中定義為公開的屬性,而是在.m的Extension中定義為私有屬性,在.h頭文件中以定義的setXXX方法的形式暴露其接口,然后又在.m文件的方法體中賦給了私有block屬性。這和直接在頭文件定義為block屬性其實應該是等效的,之所以這么寫,難道是這樣更安全?
其次,在閱讀源碼時我一直困惑于一個問題:
既然在創建session時,設self
,即AFURLSessionManager
為其代理,那為什么還要再來個AFURLSessionManagerTaskDelegate
類,在這個代理類中將部分代理方法再實現一遍?
而且這個代理類是和AFURLSessionManager
類寫在一個文件里的,剛開始讀時沒注意到,閱讀時有種剪不斷理還亂的感覺。所以這個問題一直困惑不解,所以我百度了又百度,終于看到別人的一篇文章有解釋這個問題。摘錄如下:
AFNetWorking(3.0)源碼分析(二)——AFURLSessionManager
AFURLSessionManager 與 AFURLSessionManagerTaskDelegate 的分工
到這里我們可以想一想,既然NSURLSession的delegate是AFURLSessionManager對象,那么為什么不在AFURLSessionManager中處理所有的事件回調,搞出來一個AFURLSessionManagerTaskDelegate干什么?
我們知道,NSURLSession的回調有很多,而當我們啟動一個task,真正想要獲取的信息是什么呢?就是網絡請求最終所返回的數據(我所進行的網絡操作成功或是失敗,服務器為我返回的數據)唄! 其它的回調,什么認證質詢,task需要新的body stream,什么request即將重定向, 統統都是浮云,都是為了我們能夠最終獲取到服務器返回的數據所服務的。
另外我們也想要知道我們的網絡請求的進度。
總結為兩點:
- 獲取最終返回數據
- 獲知當前請求的進度
于是,AFNetWorking 便在紛繁復雜的回調處理中,特意抽象出AFURLSessionManagerTaskDelegate,用于應付網絡返回數據的處理(包括保存中間值,序列化返回值,錯誤處理)和網絡請求進度。
這樣做可以使代碼結構更分明,邏輯更清晰。
真是謝謝這位博主。說起來其實就是分工,將我們最終感興趣的東西(服務器返回的數據,進度,錯誤信息等)抽離在代理類中。
結尾
本篇將AFURLSessionManager
類說得差不多了,好累,洗洗睡。明天寫下一篇,整理整理AFURLSessionManagerTaskDelegate
類。