AFNetworking源码阅读3——代理

作者: Wang66 | 来源:发表于2016-10-14 17:12 被阅读368次

    前言

    在上篇AFNetworking源码阅读2——核心快结尾时,解释了为什么在AFURLSessionManager类中实现了session和 session task的代理方法,还要在AFURLSessionManagerTaskDelegate代理类中再实现一遍session task的代理方法?AFURLSessionManagerTaskDelegate代理类存在的意义是什么?
    原因我们已经知道,是将我们请求所得的数据,或感兴趣的信息等抽离在一个专门类中。然后再以此类回调给使用者调用的方法,回馈给使用者。
    该代理类的作用基本就是这样,但我们还是需要看看具体实现,学习学习。


    源码

    先看头文件:

    屏幕快照 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
    

    首先可以看到该类是实现了session task的三个协议的。manager意为对应的AFURLSessionManager实例;mutableData意为从网络返回的数据;uploadProgressdownloadProgress意为上传进度和下载进度;downloadFileURL意为所下载文件的磁盘路径;后面几个是定义的block属性,顾名思义,它们分别代表下载完成后的回调block,上传进度发生改变时的回调block,下载进度发生改变时的回调block,完成后的处理回调block。它们的类型定义如下,留心其返回值和参数。

    typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
    
    typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
    
    typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
    

    接下来该类具体怎么实现的:

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

    可以看到,该类的结构比较简单,篇幅也不长。主要有三部分:初始化,进度跟踪,代理方法的实现。我们按顺序来,先看看初始化方法:

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

    初始化方法没什么可说的,主要是在其中初始化了属性。
    接着看对task对应的进度追踪代码:

    #pragma mark - NSProgress Tracking
    
    /*
     主要是设置两个NSProgress类型变量的uploadProgress和downloadProgress属性
    */
    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
    
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
        
        /*
         下面设置了uploadProgress和downloadProgress的三个属性:cancel/pause/resume。正好对应session task的cancel/pause/resume三个状态。也就是说进度progress的数据来源于实际上由session task来驱动
         */
        [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:这个方法代码很长,但是内容却比较简单且好理解。简单说就是将下载任务task的数据总量、上传/下载进度等信息赋值给代理类的uploadProgressdownloadProgress属性。除此外,并且还实现了uploadProgressdownloadProgress俩属性取消暂停重启操作的block回调,可以看到它俩这三个操作实际上是由对应的task实现的。也就是说,代理类的进度信息的数据既是task赋给的,其操作动作也是task驱动的。总之,把一切使用者感兴趣的东西都从task抽离出去,置给了代理类。

    还没完,在这个方法结尾,给进度的属性fractionCompleted“完成度百分比”添加了监听,即KVO。只要下载百分比变化,就执行下面监听的代理方法,执行self.downloadProgressBlock(object);,调用其block回调属性。这里object便是所监听的属性的主人,即downloadProgressuploadProgress。如此该代理类的uploadProgressBlockdownloadProgressBlock俩block回调属性便拥有进度数据了。想一下,该代理类不是有uploadProgressdownloadProgress属性吗?为什么还要多此一举,去观察该进度的完成百分比,然后再将进度赋值给进度对应的blockdownloadProgressBlockuploadProgressBlock呢?这是因为我们最终给使用者调用的接口方法是以该类型block回调的,在这里完成block的赋值,然后将回调回去回传给使用层时,直接赋值给同类型的block就行了。

    最后来看session task、session dataTask和session downTask代理方法的实现。先看session task代理方法的实现:

    - (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,则说明文件下载在磁盘了,downloadFileURL为其路径;反之,数据是存在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];
                    });
                });
            });
        }
    }
    

    这个方法里代码很长,但仔细看看,其实也不复杂。其实就是数据获取完成了,想发送一个通知AFNetworkingTaskDidCompleteNotification,发送该通知时要携带一个NSDictionary型的参数信息userInfo,而前面的一大串代码都是为了给该参数赋值。而发出的这个通知,主要用于通知框架里的UI层。

    上面的代码使我比较困惑的是?:符号的含义,x?:y == x?x:y。一开始我以为这就是个普通的三元运算符,表示无论x真假,都执行y,这样理解是错误的,应当是若x成立,则执行x,否则执行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];
                });
            });
    

    这段代码里?:符号所表示的逻辑是:若存在自定义的任务完成时的block回调所在的completionGroup,则用自定义的;否则用一个方法生成一个专门的,用于完成任务时的block回调所在的group。同理,后半句表示若存在自定义的任务完成时的block回调所在的completionQueue,则用自定义的;否则用main queue。

    这里的逻辑实际上在AFURLSessionManager头文件里定义这俩属性时便在注释里已有说明:

    /**
     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代理类算是看完了,也清楚了它的作用就是把请求网络所返回的数据信息或者错误信息抽离至这个代理类中。现在数据我们看见是抽离在该类里了,但是它是怎么回传给使用者的,我们最好再次梳理一下。

    使用者是调用HTTP的便利方法来请求网络获取响应数据的,而响应的数据信息或错误信息是通过downloadProgresssuccessfailure三个block回调的。

    - (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实例,不过参数更丰富灵活。
        NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                            URLString:URLString
                                                           parameters:parameters
                                                       uploadProgress:nil
                                                     downloadProgress:downloadProgress
                                                              success:success
                                                              failure:failure];
    
    ...
    ...
    

    这三个block回调的数据信息从哪里得来呢?我们跳进方法里可以看到,它的block数据便从里面这个方法的block而来。

    我们再跳进里面这个方法观察。可以看到它的block回调数据同样来自一个内部方法的block回调:

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

    我们继续,再跳进这个方法观察。它的block数据依旧来源于内部一个方法的block回调。

    - (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];
    ...
    ...
    

    我们再继续,跳进该方法,一切清晰了:该方法的block并没有来自其他的方法回调了,而是来自本篇所讲的代理类的。也就是说,是在这个方法里,请求任务对应的响应数据或错误信息等开始从代理类流出,一层一层,从内至外的回传给使用者调用的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
    {
        /*
         创建一个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.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    

    结尾

    本篇继续解释了代理类存在的作用,并解释了一些具体实现,最后再次梳理了下响应数据的流向。现在算是把AFURLSessionManagerTaskDelegate类说完了。下篇学习学习序列化。

    相关文章

      网友评论

        本文标题:AFNetworking源码阅读3——代理

        本文链接:https://www.haomeiwen.com/subject/hpwgyttx.html