AFNetworking源碼閱讀3——代理

前言

在上篇AFNetworking源碼閱讀2——核心快結(jié)尾時(shí),解釋了為什么在AFURLSessionManager類中實(shí)現(xiàn)了session和 session task的代理方法,還要在AFURLSessionManagerTaskDelegate代理類中再實(shí)現(xiàn)一遍session task的代理方法?AFURLSessionManagerTaskDelegate代理類存在的意義是什么?
原因我們已經(jīng)知道,是將我們請(qǐng)求所得的數(shù)據(jù),或感興趣的信息等抽離在一個(gè)專門類中。然后再以此類回調(diào)給使用者調(diào)用的方法,回饋給使用者。
該代理類的作用基本就是這樣,但我們還是需要看看具體實(shí)現(xiàn),學(xué)習(xí)學(xué)習(xí)。


源碼

先看頭文件:

屏幕快照 2016-10-14 下午2.02.21.png
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
@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; // 所下載文件的磁盤路徑
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
@end

首先可以看到該類是實(shí)現(xiàn)了session task的三個(gè)協(xié)議的。manager意為對(duì)應(yīng)的AFURLSessionManager實(shí)例;mutableData意為從網(wǎng)絡(luò)返回的數(shù)據(jù);uploadProgressdownloadProgress意為上傳進(jìn)度和下載進(jìn)度;downloadFileURL意為所下載文件的磁盤路徑;后面幾個(gè)是定義的block屬性,顧名思義,它們分別代表下載完成后的回調(diào)block,上傳進(jìn)度發(fā)生改變時(shí)的回調(diào)block,下載進(jìn)度發(fā)生改變時(shí)的回調(diào)block,完成后的處理回調(diào)block。它們的類型定義如下,留心其返回值和參數(shù)。

typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);

typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);

typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);

接下來(lái)該類具體怎么實(shí)現(xiàn)的:

屏幕快照 2016-10-14 下午2.03.21.png

可以看到,該類的結(jié)構(gòu)比較簡(jiǎn)單,篇幅也不長(zhǎng)。主要有三部分:初始化,進(jìn)度跟蹤,代理方法的實(shí)現(xiàn)。我們按順序來(lái),先看看初始化方法:

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }
    self.mutableData = [NSMutableData data];
    self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
    self.uploadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;

    self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
    self.downloadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown;
    return self;
}

初始化方法沒(méi)什么可說(shuō)的,主要是在其中初始化了屬性。
接著看對(duì)task對(duì)應(yīng)的進(jìn)度追蹤代碼:

#pragma mark - NSProgress Tracking

/*
 主要是設(shè)置兩個(gè)NSProgress類型變量的uploadProgress和downloadProgress屬性
*/
- (void)setupProgressForTask:(NSURLSessionTask *)task {
    __weak __typeof__(task) weakTask = task;

    self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
    self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
    
    /*
     下面設(shè)置了uploadProgress和downloadProgress的三個(gè)屬性:cancel/pause/resume。正好對(duì)應(yīng)session task的cancel/pause/resume三個(gè)狀態(tài)。也就是說(shuō)進(jìn)度progress的數(shù)據(jù)來(lái)源于實(shí)際上由session task來(lái)驅(qū)動(dòng)
     */
    [self.uploadProgress setCancellable:YES];
    [self.uploadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.uploadProgress setPausable:YES];
    [self.uploadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];
    if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.uploadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    [self.downloadProgress setCancellable:YES];
    [self.downloadProgress setCancellationHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask cancel];
    }];
    [self.downloadProgress setPausable:YES];
    [self.downloadProgress setPausingHandler:^{
        __typeof__(weakTask) strongTask = weakTask;
        [strongTask suspend];
    }];

    if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
        [self.downloadProgress setResumingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask resume];
        }];
    }

    /*
     觀察progress的fractionCompleted屬性
    */
    [self.downloadProgress addObserver:self
                            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                               options:NSKeyValueObservingOptionNew
                               context:NULL];
    [self.uploadProgress addObserver:self
                          forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
}

- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
    [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
   if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
    else if ([object isEqual:self.uploadProgress]) {
        if (self.uploadProgressBlock) {
            self.uploadProgressBlock(object);
        }
    }
}

setupProgressForTask:這個(gè)方法代碼很長(zhǎng),但是內(nèi)容卻比較簡(jiǎn)單且好理解。簡(jiǎn)單說(shuō)就是將下載任務(wù)task的數(shù)據(jù)總量、上傳/下載進(jìn)度等信息賦值給代理類的uploadProgressdownloadProgress屬性。除此外,并且還實(shí)現(xiàn)了uploadProgressdownloadProgress倆屬性取消暫停、重啟操作的block回調(diào),可以看到它倆這三個(gè)操作實(shí)際上是由對(duì)應(yīng)的task實(shí)現(xiàn)的。也就是說(shuō),代理類的進(jìn)度信息的數(shù)據(jù)既是task賦給的,其操作動(dòng)作也是task驅(qū)動(dòng)的。總之,把一切使用者感興趣的東西都從task抽離出去,置給了代理類。

還沒(méi)完,在這個(gè)方法結(jié)尾,給進(jìn)度的屬性fractionCompleted“完成度百分比”添加了監(jiān)聽(tīng),即KVO。只要下載百分比變化,就執(zhí)行下面監(jiān)聽(tīng)的代理方法,執(zhí)行self.downloadProgressBlock(object);,調(diào)用其block回調(diào)屬性。這里object便是所監(jiān)聽(tīng)的屬性的主人,即downloadProgressuploadProgress。如此該代理類的uploadProgressBlockdownloadProgressBlock倆block回調(diào)屬性便擁有進(jìn)度數(shù)據(jù)了。想一下,該代理類不是有uploadProgressdownloadProgress屬性嗎?為什么還要多此一舉,去觀察該進(jìn)度的完成百分比,然后再將進(jìn)度賦值給進(jìn)度對(duì)應(yīng)的blockdownloadProgressBlockuploadProgressBlock呢?這是因?yàn)槲覀冏罱K給使用者調(diào)用的接口方法是以該類型block回調(diào)的,在這里完成block的賦值,然后將回調(diào)回去回傳給使用層時(shí),直接賦值給同類型的block就行了。

最后來(lái)看session task、session dataTask和session downTask代理方法的實(shí)現(xiàn)。先看session task代理方法的實(shí)現(xiàn):

- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;

    __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;
    }

    // 若有downloadFileURL,則說(shuō)明文件下載在磁盤了,downloadFileURL為其路徑;反之,數(shù)據(jù)是存在data里的
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

        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];
                });
            });
        });
    }
}

這個(gè)方法里代碼很長(zhǎng),但仔細(xì)看看,其實(shí)也不復(fù)雜。其實(shí)就是數(shù)據(jù)獲取完成了,想發(fā)送一個(gè)通知AFNetworkingTaskDidCompleteNotification,發(fā)送該通知時(shí)要攜帶一個(gè)NSDictionary型的參數(shù)信息userInfo,而前面的一大串代碼都是為了給該參數(shù)賦值。而發(fā)出的這個(gè)通知,主要用于通知框架里的UI層。

上面的代碼使我比較困惑的是?:符號(hào)的含義,x?:y == x?x:y。一開(kāi)始我以為這就是個(gè)普通的三元運(yùn)算符,表示無(wú)論x真假,都執(zhí)行y,這樣理解是錯(cuò)誤的,應(yīng)當(dāng)是若x成立,則執(zhí)行x,否則執(zhí)行y。

        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];
            });
        });

這段代碼里?:符號(hào)所表示的邏輯是:若存在自定義的任務(wù)完成時(shí)的block回調(diào)所在的completionGroup,則用自定義的;否則用一個(gè)方法生成一個(gè)專門的,用于完成任務(wù)時(shí)的block回調(diào)所在的group。同理,后半句表示若存在自定義的任務(wù)完成時(shí)的block回調(diào)所在的completionQueue,則用自定義的;否則用main queue。

這里的邏輯實(shí)際上在AFURLSessionManager頭文件里定義這倆屬性時(shí)便在注釋里已有說(shuō)明:

/**
 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;

至此,我們把AFURLSessionManagerTaskDelegate代理類算是看完了,也清楚了它的作用就是把請(qǐng)求網(wǎng)絡(luò)所返回的數(shù)據(jù)信息或者錯(cuò)誤信息抽離至這個(gè)代理類中。現(xiàn)在數(shù)據(jù)我們看見(jiàn)是抽離在該類里了,但是它是怎么回傳給使用者的,我們最好再次梳理一下。

使用者是調(diào)用HTTP的便利方法來(lái)請(qǐng)求網(wǎng)絡(luò)獲取響應(yīng)數(shù)據(jù)的,而響應(yīng)的數(shù)據(jù)信息或錯(cuò)誤信息是通過(guò)downloadProgresssuccess、failure三個(gè)block回調(diào)的。

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{

    // 該方法的目的仍是生成dataTask實(shí)例,不過(guò)參數(shù)更豐富靈活。
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];

...
...

這三個(gè)block回調(diào)的數(shù)據(jù)信息從哪里得來(lái)呢?我們跳進(jìn)方法里可以看到,它的block數(shù)據(jù)便從里面這個(gè)方法的block而來(lái)。

我們?cè)偬M(jìn)里面這個(gè)方法觀察??梢钥吹剿腷lock回調(diào)數(shù)據(jù)同樣來(lái)自一個(gè)內(nèi)部方法的block回調(diào):

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
...
...
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];
...
...

我們繼續(xù),再跳進(jìn)這個(gè)方法觀察。它的block數(shù)據(jù)依舊來(lái)源于內(nèi)部一個(gè)方法的block回調(diào)。

- (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 {

    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
...
...

我們?cè)倮^續(xù),跳進(jìn)該方法,一切清晰了:該方法的block并沒(méi)有來(lái)自其他的方法回調(diào)了,而是來(lái)自本篇所講的代理類的。也就是說(shuō),是在這個(gè)方法里,請(qǐng)求任務(wù)對(duì)應(yīng)的響應(yīng)數(shù)據(jù)或錯(cuò)誤信息等開(kāi)始從代理類流出,一層一層,從內(nèi)至外的回傳給使用者調(diào)用的HTTP便利接口方法,回傳給使用者。

- (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.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

結(jié)尾

本篇繼續(xù)解釋了代理類存在的作用,并解釋了一些具體實(shí)現(xiàn),最后再次梳理了下響應(yīng)數(shù)據(jù)的流向。現(xiàn)在算是把AFURLSessionManagerTaskDelegate類說(shuō)完了。下篇學(xué)習(xí)學(xué)習(xí)序列化。

最后編輯于
?著作權(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)容

  • 概述 上一篇主要分析了基于NSURLConnection的AFURLConnectionOperation,本篇主...
    egoCogito_panf閱讀 2,110評(píng)論 0 13
  • 說(shuō)到AFNetwokring這個(gè)強(qiáng)大第三方網(wǎng)絡(luò)請(qǐng)求庫(kù),大家應(yīng)該都不陌生吧,ios開(kāi)發(fā)、mac開(kāi)發(fā)都經(jīng)常用,主要是他...
    塵峰的小孩閱讀 521評(píng)論 0 0
  • 前言 在AFNetworking源碼閱讀1中,我們已經(jīng)閱讀完了AFHTTPSessionManager類。本篇我們...
    Wang66閱讀 1,170評(píng)論 0 8
  • 本篇是AFNetworking 3.0源碼解讀的第五篇了。 AFNetworking 3.0 源碼解讀(一)之 A...
    泥孩兒0107閱讀 610評(píng)論 0 0
  • 寫在開(kāi)頭: 作為一個(gè)iOS開(kāi)發(fā),也許你不知道NSUrlRequest、不知道NSUrlConnection、也不知...
    涂耀輝閱讀 99,166評(píng)論 172 1,348

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