AFNetworking源碼閱讀2——核心

前言

AFNetworking源碼閱讀1中,我們已經(jīng)閱讀完了AFHTTPSessionManager類。本篇我們閱讀其父類AFURLSessionManager。該類是整個(gè)框架的核心。


源碼:AFURLSessionManager

先來看看頭文件AFURLSessionManager.h

屏幕快照 2016-10-10 下午2.06.59.png

該類代碼太長(zhǎng),我們從頭至尾一段一段看。先看頭部的屬性部分:

@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; // 代理回調(diào)所運(yùn)行的操作隊(duì)列

@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;

第一個(gè)屬性session就是該類所管理的session;operationQueue是代理回調(diào)所運(yùn)行的操作隊(duì)列;responseSerializer是解析網(wǎng)絡(luò)返回?cái)?shù)據(jù)的序列化器,它是實(shí)現(xiàn)了AFURLResponseSerialization協(xié)議的任意類型;securityPolicy有關(guān)網(wǎng)絡(luò)安全連接的安全策略,這個(gè)后面還要繼續(xù)研究;reachabilityManager是檢測(cè)網(wǎng)絡(luò)狀態(tài)的檢測(cè)器,后面也會(huì)繼續(xù)研究;tasks代表管理的session此時(shí)正在運(yùn)行的data task,upload task,download task們;completionQueuecompletionBlock的dispatch queue,而completionGroupcompletionBlock的dispatch group。

屬性看完,該輪到初始化方法了:

#pragma mark - init method
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks; // 選擇性地取消掛起的task

可以看到,第一個(gè)方法為初始化方法,第二個(gè)方法為選擇性的取消掛起的session task,只需要傳入某個(gè)掛起的task為參數(shù),便可取消該task。
這里需要多說一句的是初始化方法后面有個(gè)標(biāo)識(shí)符:NS_DESIGNATED_INITIALIZER,它代表什么意思呢?它呀,意為“被設(shè)計(jì)的初始化器”,“被指定的初始化方法”。
詳情請(qǐng)查看:iOS: 聊聊 Designated Initializer(指定初始化函數(shù))
正確使用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;

// 為一個(gè)本地文件通過構(gòu)成一個(gè)特定request,從而創(chuàng)建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下也可能提供多個(gè)不同場(chǎng)景的方法。(篇幅有限,部分方法省略)
多說一句:DEPRECATED_ATTRIBUTE標(biāo)識(shí)符的意思是慢慢棄用的屬性或接口,如果我們使用了,在xcode中就會(huì)出現(xiàn)警告??信息。

接下來是這兩個(gè)方法:

#pragma mark - get progress for task

- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;

- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;

這兩個(gè)方法看名字就可以明白其意義:獲得某個(gè)task的進(jìn)度。

再接著就是session delegate callback的setter方法了,它們是和session以及session task的代理方法是一一對(duì)應(yīng)的。在這里不作說明,等到閱讀到代理方法時(shí)就明白了。

接下來就是最后一部分代碼:


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個(gè)有效方法》筆記1——熟悉Objective-C。但在這里聲明時(shí)并沒有用extern**,而是用了FOUNDATION_EXPORT,關(guān)于它的解釋見:FOUNDATION_EXPORT 或#define 或 extern


現(xiàn)在開始看AFURLSessionManager.m
代碼太長(zhǎng)太長(zhǎng),我們一段一段來分析。先來看看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

這里又定義了一個(gè)readwrite的session作為私有屬性。而在頭文件中定義的session屬性是readonly的。這么做更安全,在內(nèi)部我們可對(duì)該屬性進(jìn)行讀寫操作,而暴露給外部時(shí),使用者只能read。
mutableTaskDelegatesKeyedByTaskIdentifier屬性是個(gè)可變字典。該屬性有大用處,以taskIdentifier作為key,以task delegate為value,將task和其delegate一對(duì)一綁定,后面還需細(xì)說。
然后后面均為回調(diào)block的屬性。

然后我們看看初始化方法:

- (instancetype)init {
    return [self initWithSessionConfiguration:nil];
}

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

    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    /*
     初始化各個(gè)屬性
     */
    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]; // 默認(rèn)的反序列化類型是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,給每個(gè)task設(shè)置一個(gè)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];
}

在初始化方法中可以看到。首先,若初始化時(shí)傳入的參數(shù)configuration為nil,則默認(rèn)初始化為defaultSessionConfiguration;然后就是初始化各個(gè)屬性了,session屬性是通過NSURLSession的類方法sessionWithConfiguration:delegate:delegateQueue:方法來初始化的,設(shè)delegate為self。當(dāng)時(shí)閱讀代碼時(shí),始終有個(gè)疑問:本類實(shí)現(xiàn)了四個(gè)協(xié)議,第一個(gè)為session的協(xié)議,是在這里設(shè)置delegate為self了。后面三個(gè)協(xié)議我始終找不到在哪設(shè)置session task的delegate為self的地方,覺得很疑惑,這沒地方設(shè)置過代理,為什么實(shí)現(xiàn)了代理方法?代理方法怎么會(huì)執(zhí)行呢?后面我從網(wǎng)上才看到,只要設(shè)置了session的delegate,那也等同于設(shè)置該session創(chuàng)建的session task的代理。還因這個(gè)問題找了半天代碼...
初始化方法的最后遍歷了所管理的session的task,給每個(gè)task設(shè)置了一個(gè)delegate。關(guān)于設(shè)置delegate這個(gè)方法的實(shí)現(xiàn)細(xì)節(jié),我們暫且等到下文詳解。但這里我也有個(gè)疑惑:這里是初始化方法,session才剛剛被初始化,該session應(yīng)當(dāng)還沒有對(duì)應(yīng)的session task啊,為何要遍歷其session task?會(huì)有嗎?這個(gè)問題百思不得其解,希望哪位大兄弟看到能解惑...

然后我們看這個(gè)類最重要的方法:

- (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.調(diào)用NSURLSession的實(shí)例方法dataTaskWithRequest生成dataTask
    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request]; 
    });

    // 2.為該dataTask設(shè)置delegate,將dataTask與delegate一一綁定
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

看到這個(gè)方法名,我們大概能猜到該方法內(nèi)部的實(shí)現(xiàn)步驟:因?yàn)閰?shù)為request,返回值時(shí)dataTask,那很可能是完成了由request生成dataTask的動(dòng)作。但是應(yīng)該還不僅僅是這樣,因?yàn)樵摲椒ㄟ€有block回調(diào),block回調(diào)的內(nèi)容是怎么來的呢?
好了,一看代碼果然和我們猜的差不多。這里面主要有兩個(gè)步驟:
1.調(diào)用NSURLSession的實(shí)例方法dataTaskWithRequest:生成dataTask。
url_session_manager_create_task_safely是為了修復(fù)某個(gè)bug,我們?cè)诖瞬槐丶m結(jié)。
2.為該dataTask設(shè)置delegate,將dataTask與delegate一一綁定。并且通過block傳遞出來該請(qǐng)求任務(wù)的一些進(jìn)度、數(shù)據(jù)、錯(cuò)誤信息等。

我們看看第二個(gè)步驟的方法實(shí)現(xiàn):

- (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
{
    /*
     創(chuàng)建一個(gè)AFURLSessionManagerTaskDelegate代理類的對(duì)象,并為幾個(gè)屬性賦值。然后調(diào)用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;
}

可以看到,里面創(chuàng)建了一個(gè)AFURLSessionManagerTaskDelegate代理類的對(duì)象,并為幾個(gè)屬性賦值(主要為block回調(diào)屬性)。然后調(diào)用setDelegate:forTask:將此delegate實(shí)例對(duì)象和該task綁定。但實(shí)現(xiàn)此功能的是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]; // 該方法主要是設(shè)置兩個(gè)NSProgress類型的變量uploadProgress和downloadProgress的屬性
    [self addNotificationObserverForTask:task]; // 給該task添加兩個(gè)KVO事件
    [self.lock unlock];
}

該方法內(nèi)部有三個(gè)步驟:
1.將task的taskIdentifier作為鍵,將delegate作為值,賦給可變字典mutableTaskDelegatesKeyedByTaskIdentifier。完成delegate和task的一對(duì)一綁定;
2.調(diào)用delegate的setupProgressForTask:方法。由task設(shè)置delegate對(duì)象的uploadProgressdownloadProgress屬性的屬性值。
3.給該task添加兩個(gè)觀察(啟動(dòng)和暫停)。
關(guān)于步驟一,相當(dāng)于對(duì)mutableTaskDelegatesKeyedByTaskIdentifier可變字典的set操作,當(dāng)然還有與此對(duì)應(yīng)的get操作,獲得某task對(duì)應(yīng)的delegate。

- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
    NSParameterAssert(task);

    AFURLSessionManagerTaskDelegate *delegate = nil;
    [self.lock lock];
    delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
    [self.lock unlock];

    return delegate;
}

該類中有多處用到了get操作,比如:
移除某個(gè)task對(duì)應(yīng)的delegate。首先通過delegateForTask:方法獲得此delegate,然后移除此task的進(jìn)度觀察,移除啟動(dòng)和暫停的觀察,并最終從字典中刪除此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的上傳或下載進(jìn)度。**

- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
    return [[self delegateForTask:task] uploadProgress];
}

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

再比如,我們待會(huì)會(huì)講到的,在該類實(shí)現(xiàn)的代理方法里,有時(shí)還需要調(diào)用在其代理類AFURLSessionManagerTaskDelegate中實(shí)現(xiàn)的代理方法。此時(shí)就需要調(diào)用delegateForTask:方法得到相應(yīng)的delegate。

關(guān)于步驟三的實(shí)現(xiàn)詳情:

- (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添加啟動(dòng)和暫停的通知,當(dāng)該task啟動(dòng)時(shí)就執(zhí)行taskDidResume:方法,當(dāng)該task暫停時(shí)就執(zhí)行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:兩個(gè)方法的代碼是什么意思,沒明白...不應(yīng)該是在某個(gè)地方發(fā)出了通知,然后在這觀察到了,然后就會(huì)執(zhí)行這兩個(gè)方法嗎?那為什么,在這倆方法里還post notification???
更新:taskDidResume:taskDidSuspend:分別是AFNSURLSessionTaskDidSuspendNotificationAFNSURLSessionTaskDidSuspendNotification的回調(diào)方法,我們上面說其意分別代表為task啟動(dòng)和暫停。為了確認(rèn),我們?cè)诖a中找找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_resumeaf_suspend是什么方法呢?我們預(yù)期它應(yīng)該是在該task執(zhí)行resumesuspend方法后便立即post通知的。我們尋根溯源,繼續(xù)找af_resumeaf_suspend倆方法是在哪觸發(fā)的:

+ (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));
    }
}

可以看到那倆方法大量基本出現(xiàn)在swizzleResumeAndSuspendMethodForClass:這個(gè)方法中,繼續(xù)找尋根溯源,原來這個(gè)方法是在_AFURLSessionTaskSwizzling這個(gè)類的load方法中調(diào)用的。_AFURLSessionTaskSwizzling這個(gè)類干了什么,我們研究研究。

屏幕快照 2016-10-13 上午10.57.48.png

說到這我們得說說Method Swizzing,我在這篇文中已經(jīng)說過:《52個(gè)有效方法》筆記3——探究runtime
摘錄如下:

OC對(duì)象都有一個(gè)isa指針,該指針指向一個(gè)方法調(diào)度表,它存儲(chǔ)著該對(duì)象的方法。并且,它的形式是鍵值對(duì),key為SEL,value為IMP。
既然如此,那我們可以在SEL和IMP的映射上做文章。也就是說我可以人為地改變SEL和IMP之間的映射關(guān)系。
這樣做有什么意義嗎?嗯,意義很大。我們可以把原有的類和我們自定義類的映射對(duì)調(diào),那這樣,即使我們?cè)诖a里調(diào)用的是原有類的方法,但暗地里卻神不知鬼不覺地在執(zhí)行我們自定義的方法。哈哈,說得有些玄乎了。
其實(shí),在實(shí)踐中Method Swizzing可以完成AOP或者對(duì)原有類的拓展。

在本例中,就將原始的resumesuspend倆方法的實(shí)現(xiàn)IMP調(diào)包了,調(diào)成了_AFURLSessionTaskSwizzling這個(gè)類自己實(shí)現(xiàn)的af_resumeaf_suspend。而在af_resumeaf_suspend倆方法里,我們分別post出了相應(yīng)的通知。如以一來,即便是程序是在調(diào)用原始的resume方法,但程序?qū)嶋H上卻神不知鬼不覺地執(zhí)行了af_resume,發(fā)送了通知出來。

另外之前看這塊代碼時(shí)覺得蒙圈的一個(gè)地方是把````taskDidResume:taskDidSuspend:倆方法里發(fā)送的通知名字看錯(cuò)了??。在這倆回調(diào)方法里post出的是AFNetworkingTaskDidResumeNotificationAFNetworkingTaskDidSuspendNotification通知,主要是用于通知UI層的。并不是AFNSURLSessionTaskDidResumeNotificationAFNSURLSessionTaskDidSuspendNotification``通知,當(dāng)時(shí)太蒙圈了,怎么在觀察到通知的回調(diào)里再次發(fā)出通知??。


現(xiàn)在,我們來看看在該類中實(shí)現(xiàn)的代理方法。在該類中實(shí)現(xiàn)了4個(gè)協(xié)議的代理方法。
有關(guān)這些代理方法的含義及具體實(shí)現(xiàn)的講解這篇文章講解的很詳細(xì),在此我不再寫了:
AFNetworking源碼閱讀(三)

但是還是有幾個(gè)點(diǎn)需要說明一下。
首先,就是我們可以看到在這些代理方法的實(shí)現(xiàn)里,很多都會(huì)先判斷有無與代理方法對(duì)應(yīng)的自定義的block回調(diào)(這個(gè)我們?cè)谇懊嬷v頭文件時(shí)提到過,那些block都是與代理方法對(duì)應(yīng)的)。若有,則調(diào)用自定義的block,在定義的block回調(diào)里做相應(yīng)的處理。
說起自定義的block回調(diào)。若按我平時(shí)的寫法習(xí)慣,我一般都是直接在頭文件中定義為公開屬性。這樣在.m文件里直接調(diào)用就行。但是這里的源碼卻不是,它沒有在頭文件中定義為公開的屬性,而是在.m的Extension中定義為私有屬性,在.h頭文件中以定義的setXXX方法的形式暴露其接口,然后又在.m文件的方法體中賦給了私有block屬性。這和直接在頭文件定義為block屬性其實(shí)應(yīng)該是等效的,之所以這么寫,難道是這樣更安全?

其次,在閱讀源碼時(shí)我一直困惑于一個(gè)問題:
既然在創(chuàng)建session時(shí),設(shè)self,即AFURLSessionManager為其代理,那為什么還要再來個(gè)AFURLSessionManagerTaskDelegate類,在這個(gè)代理類中將部分代理方法再實(shí)現(xiàn)一遍?
而且這個(gè)代理類是和AFURLSessionManager類寫在一個(gè)文件里的,剛開始讀時(shí)沒注意到,閱讀時(shí)有種剪不斷理還亂的感覺。所以這個(gè)問題一直困惑不解,所以我百度了又百度,終于看到別人的一篇文章有解釋這個(gè)問題。摘錄如下:
AFNetWorking(3.0)源碼分析(二)——AFURLSessionManager

AFURLSessionManager 與 AFURLSessionManagerTaskDelegate 的分工

到這里我們可以想一想,既然NSURLSession的delegate是AFURLSessionManager對(duì)象,那么為什么不在AFURLSessionManager中處理所有的事件回調(diào),搞出來一個(gè)AFURLSessionManagerTaskDelegate干什么?

我們知道,NSURLSession的回調(diào)有很多,而當(dāng)我們啟動(dòng)一個(gè)task,真正想要獲取的信息是什么呢?就是網(wǎng)絡(luò)請(qǐng)求最終所返回的數(shù)據(jù)(我所進(jìn)行的網(wǎng)絡(luò)操作成功或是失敗,服務(wù)器為我返回的數(shù)據(jù))唄! 其它的回調(diào),什么認(rèn)證質(zhì)詢,task需要新的body stream,什么request即將重定向, 統(tǒng)統(tǒng)都是浮云,都是為了我們能夠最終獲取到服務(wù)器返回的數(shù)據(jù)所服務(wù)的。

另外我們也想要知道我們的網(wǎng)絡(luò)請(qǐng)求的進(jìn)度。
總結(jié)為兩點(diǎn):

  1. 獲取最終返回?cái)?shù)據(jù)
  2. 獲知當(dāng)前請(qǐng)求的進(jìn)度

于是,AFNetWorking 便在紛繁復(fù)雜的回調(diào)處理中,特意抽象出AFURLSessionManagerTaskDelegate,用于應(yīng)付網(wǎng)絡(luò)返回?cái)?shù)據(jù)的處理(包括保存中間值,序列化返回值,錯(cuò)誤處理)和網(wǎng)絡(luò)請(qǐng)求進(jìn)度。
這樣做可以使代碼結(jié)構(gòu)更分明,邏輯更清晰。

真是謝謝這位博主。說起來其實(shí)就是分工,將我們最終感興趣的東西(服務(wù)器返回的數(shù)據(jù),進(jìn)度,錯(cuò)誤信息等)抽離在代理類中。


結(jié)尾

本篇將AFURLSessionManager類說得差不多了,好累,洗洗睡。明天寫下一篇,整理整理AFURLSessionManagerTaskDelegate類。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容