美文网首页将来跳槽用iOS接下来要研究的知识点
【源码解读】AFNetWorking ─── 网络请求

【源码解读】AFNetWorking ─── 网络请求

作者: WellsCai | 来源:发表于2018-01-20 08:11 被阅读24次

    一. 前言

    AFNetWorking的使用率非常高,这个不必多说。无论是AFNetWorking还是SDWebImage等涉及到网络请求的,底层都是封装了原生的网络请求。如果对原生的网络请求还不熟悉的话,可以看看我之前写的这篇文章【iOS小结】NSURLSession
    AFNetWorking的功能很多,我这篇主要解读的是网络请求(关于AFNetWorking怎么封装网络请求的知识)。

    二. AFNetWorking的网络请求

    网络请求涉及的类主要是这两个:

    • AFHTTPSessionManager:拥有生成任务,管理任务的功能,并提供HTTP方法的API接口
    • AFURLSessionManager:封装了生成任务,管理任务
    网络请求部分.png

    我们先来看一个简单的POST请求,我们只需把urlStringparameters传进去,AFHTTPSessionManager就会创建一个POST的请求任务,并把请求进度,请求成功,请求失败通过Block回调给你。
    简单来说,其内部的实现其实是会通过这两个参数生成对应的NSURLRequest,并通过NSURLSession创建请求任务,遵守相关的NSURLSession的协议,在对应的协议方法里面进行判断处理,回调。

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //设置URL
    NSString *urlString = @"www.baidu.com/login/info";
    //设置参数
    NSDictionary *parameters = @{@"username":@"Tom",@"pwd":@"123"};
    //发送post请求
    [manager POST:urlString parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
        //上传过程中的回调
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //成功的回调
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //失败的回调
    }];
    

    接下来我们来一步步分析:

    AFHTTPSessionManager

    当使用AFNetWorking进行网络请求时,我们会创建AFHTTPSessionManager对象,进行对应的HTTP请求。AFHTTPSessionManager继承于AFURLSessionManager,除了AFURLSessionManager的功能外,还提供HTTP方法的接口。
    以下红框是AFHTTPSessionManager提供的方法,以及对应的用途:


    HTTP支持的方法.png

    我们再来看具体提供的方法:

    //GET方法
    - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                       parameters:(nullable id)parameters
                          success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                          failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
    //GET方法(带下载进度)
    - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                                parameters:(nullable id)parameters
                                  progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                                   success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                   failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    //HEAD方法
    - (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                        parameters:(nullable id)parameters
                           success:(nullable void (^)(NSURLSessionDataTask *task))success
                           failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    //POST方法
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(nullable id)parameters
                           success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                           failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
    //POST方法(带上传进度)
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                                 parameters:(nullable id)parameters
                                   progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                    success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                    failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    //POST方法(带构造体Block)→ 用于Multipart/form-data
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(nullable id)parameters
         constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                           success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                           failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
    //POST方法(带构造体Block和上传进度)→ 用于Multipart/form-data
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                                 parameters:(nullable id)parameters
                  constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                                   progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                    success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                    failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    //PUT方法
    - (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                       parameters:(nullable id)parameters
                          success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                          failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    //PATCH方法
    - (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                         parameters:(nullable id)parameters
                            success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                            failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    //DELETE方法
    - (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                          parameters:(nullable id)parameters
                             success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                             failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    

    其中比较特殊的是POST方法,POST带构造体Block的方法是用于Multipart/form-data,用于上传文件。具体原理我就不多解释,可以看看这个例子AFNetworking文件上传Multipart/form-data POST文件上传详解

    另外,这两种方法创建的是uploadTask(通过文件流上传)。在下面方法中通过请求序列化对象self.requestSerializer将url、参数和构造体Block序列化成对应的request,然后通过该request去创建对应的uploadTask。

    // POST请求(带构造体block,带进度,用于Multipart/form-data,上传)
    // 用于Multipart/form-data上传的基础方法
    - (NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(id)parameters
         constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                          progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
                           success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                           failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
    {
        NSError *serializationError = nil;
        //序列化request(构建)
        NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
        //判断序列化是否成功
        if (serializationError) {
            //失败的话就通过failure回调,返回nil
            if (failure) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        //创建uploadTask(通过文件流上传文件)
        __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
            if (error) {
                if (failure) {
                    failure(task, error);
                }
            } else {
                if (success) {
                    success(task, responseObject);
                }
            }
        }];
        [task resume];
        return task;
    }
    

    而其他方法创建的是dataTask。也是通过请求序列化对象self.requestSerializer将url、参数序列化成对应的request,然后通过该request去创建对应的dataTask。

    // 所有请求的基础方法(通过改变method参数)
    - (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
    {
        NSError *serializationError = nil;
        //把参数,还有各种东西转换为一个request
        NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
        
        //转换错误就在failure中回调
        if (serializationError) {
            if (failure) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                //如果解析错误,直接返回
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        //生成一个dataTask
        __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);
                }
            }
        }];
        return dataTask;
    }
    

    总之,AFHTTPSessionManager封装了几种我们常用的网络请求以及文件上传的接口(当然也是不全的,比如少了downloadTask,这边就需要我们直接使用AFHTTPRequestSerializer去创建)。
    通过AFHTTPRequestSerializer对象通过我们传入的参数转化成对应的NSMutableURLRequest。然后通过request调用其父类AFURLSessionManager创建对应的task。当然也把对应的success和failure等回调传进去。

    另外,AFHTTPSessionManager有三个属性在初始化时会被创建,baseURL好理解,requestSerializer是用来序列化(构建)request的,
    responseSerializer是用来处理响应数据的。requestSerializer是AFHTTPSessionManager特有的属性,因为AFHTTPSessionManager赋值构建request,然后生成对应的task,而responseSerializer实际上是用在AFURLSessionManager,因为它那边负责生成管理任务,并且对结果进行处理。

    self.baseURL = url;
    // 设置请求和响应序列化对象
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    
    AFURLSessionManager

    其实从AFHTTPSessionManager先分析,再分析AFURLSessionManager是有弊端的,因为AFHTTPSessionManager继承于AFURLSessionManager,所以AFHTTPSessionManager的很多功能只有留到讲解AFURLSessionManager的时候才能说明。

    之前AFHTTPSessionManager的分析说过,AFURLSessionManager封装了生成、管理任务。AFHTTPSessionManager只需要生成对应request,通过request调用AFURLSessionManager生成task的方法。
    AFURLSessionManager是一个怎么样的类呢?
    这边的探究分成两部分:
    ① 提供什么功能(其实还是跟任务有关)→看.h
    ② 怎么实现这些功能 →看.m

    ① AFURLSessionManager提供的功能

    AFURLSessionManager提供的功能也是之前AFHTTPSessionManager未讲到的功能。AFURLSessionManager主要是封装了生成任务和管理任务,AFURLSessionManager实际上是NSURLSession对象的组合体,提供的功能自然也是围绕着任务。
    AFURLSessionManager提供的功能主要有三种:
    1)获取正在执行的所有任务、请求任务、上传任务、下载任务。

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

    2)提供多种创建task的方法(封装了所有NSURLSession创建task的方法)
    3)提供了对NSURLSession协议的自定义处理的Block。(类似于代理)

    ② AFURLSessionManager怎么实现这些功能

    AFURLSessionManager.m的内容其实挺多的,分开来看其实主要有四部分内容(最主要还是第四部分):
    1)单例创建各种队列的C函数。定义各种key,Notification的值。定义各种Block。(该部分也好理解,就是为了让下面来使用)
    2)_AFURLSessionTaskSwizzling(主要用来交换NSURLSessionTask的Resume和Suspend方法)→主要是为了添加通知的代码?

    + (void)load {
        /**
         WARNING: 高能预警
         https://github.com/AFNetworking/AFNetworking/pull/2702
         */
        // 担心以后iOS中不存在NSURLSessionTask
        if (NSClassFromString(@"NSURLSessionTask")) {
            /**
             iOS 7和iOS 8在NSURLSessionTask实现上有些许不同,这使得下面的代码实现略显trick
             关于这个问题,大家做了很多Unit Test,足以证明这个方法是可行的
             目前我们所知的:
                - NSURLSessionTasks是一组class的统称,如果你仅仅使用提供的API来获取NSURLSessionTask的class,并不一定返回的是你想要的那个(获取NSURLSessionTask的class目的是为了获取其resume方法)
                - 简单地使用[NSURLSessionTask class]并不起作用。你需要新建一个NSURLSession,并根据创建的session再构建出一个NSURLSessionTask对象才行。
                - iOS 7上,localDataTask(下面代码构造出的NSURLSessionDataTask类型的变量,为了获取对应Class)的类型是 __NSCFLocalDataTask,__NSCFLocalDataTask继承自__NSCFLocalSessionTask,__NSCFLocalSessionTask继承自__NSCFURLSessionTask。
                - iOS 8上,localDataTask的类型为__NSCFLocalDataTask,__NSCFLocalDataTask继承自__NSCFLocalSessionTask,__NSCFLocalSessionTask继承自NSURLSessionTask
              - iOS 7上,__NSCFLocalSessionTask和__NSCFURLSessionTask是仅有的两个实现了resume和suspend方法的类,另外__NSCFLocalSessionTask中的resume和suspend并没有调用其父类(即__NSCFURLSessionTask)方法,这也意味着两个类的方法都需要进行method swizzling。
                - iOS 8上,NSURLSessionTask是唯一实现了resume和suspend方法的类。这也意味着其是唯一需要进行method swizzling的类
                - 因为NSURLSessionTask并不是在每个iOS版本中都存在,所以把这些放在此处(即load函数中),比如给一个dummy class添加swizzled方法都会变得很方便,管理起来也方便。
            
             一些假设前提:
                - 目前iOS中resume和suspend的方法实现中并没有调用对应的父类方法。如果日后iOS改变了这种做法,我们还需要重新处理
                - 没有哪个后台task会重写resume和suspend函数
             
             */
            // 1) 首先构建一个NSURLSession对象session,再通过session构建出一个_NSCFLocalDataTask变量
            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
            // 2) 获取到af_resume实现的指针
            IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
            Class currentClass = [localDataTask class];
            // 3) 检查当前class是否实现了resume。如果实现了,继续第4步。
            while (class_getInstanceMethod(currentClass, @selector(resume))) {
                // 4) 获取到当前class的父类(superClass)
                Class superClass = [currentClass superclass];
                // 5) 获取到当前class对于resume实现的指针
                IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
                //  6) 获取到父类对于resume实现的指针
                IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
                // 7) 如果当前class对于resume的实现和父类不一样(类似iOS7上的情况),并且当前class的resume实现和af_resume不一样,才进行method swizzling。
                if (classResumeIMP != superclassResumeIMP &&
                    originalAFResumeIMP != classResumeIMP) {
                    [self swizzleResumeAndSuspendMethodForClass:currentClass];
                }
                // 8) 设置当前操作的class为其父类class,重复步骤3~8
                currentClass = [currentClass superclass];
            }
            
            [localDataTask cancel];
            [session finishTasksAndInvalidate];
        }
    }
    
    + (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));
        }
    }
    
    - (NSURLSessionTaskState)state {
        //在实际的虚拟类中不应调用状态方法。
        NSAssert(NO, @"State method should never be called in the actual dummy class");
        // 初始状态是NSURLSessionTaskStateCanceling;
        return NSURLSessionTaskStateCanceling;
    }
    
    - (void)af_resume {
        NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
        NSURLSessionTaskState state = [self state];
        //执行resume方法
        [self af_resume];
        
        //如果之前是其他状态,就变回resume状态,发出DidResume的通知
        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];
        //执行suspend方法
        [self af_suspend];
        
        //如果之前是其他状态,就变回suspend状态,发出DidSuspend的通知
        if (state != NSURLSessionTaskStateSuspended) {
            [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
        }
    }
    

    3)AFURLSessionManagerTaskDelegate(task代理类)
    为什么是task的代理类呢?主要是因为task将它将处理获取数据,监听进度的职责放在该类去执行,AFURLSessionManagerTaskDelegate是task的代理人。我们先来看AFURLSessionManager里面的方法:(同理对应的uploadTask和downloadTask也有设置的方法)

    //设置普通请求task的代理人
    - (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 *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
    
        dataTask.taskDescription = self.taskDescriptionForSessionTasks;
        //为task设置代理(把dataTask传给delegate)
        [self setDelegate:delegate forTask:dataTask];
    
        delegate.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    

    可以说task该处理的回调都放在代理类这边处理了。我们再看看这个代理类怎么处理数据和监听进度。

    关于进度的监听,AFURLSessionManagerTaskDelegate设置了两个NSProgress对象(uploadProgress和downloadProgress)来分别监听上传和下载进度。(关于NSProgress的作用可以看这篇文章进度: NSProgress
    简单说起来,NSProgress是一个记录进度的类,在需要记录多个进度有很大的优势。uploadProgress和downloadProgress除了记录进度外,通过设置也可以控制task的生命周期。
    另外,通过对task接收到的字节数、期望接收到的字节数、发送的字节数、期望发送的字节数进行监听,值改变时去设置uploadProgress或downloadProgress对应的总值或完成值。通过对uploadProgress和downloadProgress完成度的监听,通过对应的uploadProgressBlock和downloadProgressBlock将进度信息回调出去。

    //为task设置进度(task的进度会受到监听)
    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
    
        //设置上传和下载进度条的总数目,暂停,取消,回复回调
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
        [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];
            }];
        }
    
        //对task接收到的字节数、期望接收到的字节数、发送的字节数、期望发送的字节数进行监听
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        //对上传和下载完成的分数(NSProgress的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];
    }
    //移除task的进度
    - (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
        //移除task接收到的字节数、期望接收到的字节数、发送的字节数、期望发送的字节数的监听
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];
        
        //移除上传,下载进度条完成的分数的监听
        [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 {
        //如果是task的监听
        if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
                self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
                self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
                self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
                self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            }
        }
        //如果是progress的监听
        else if ([object isEqual:self.downloadProgress]) {
            if (self.downloadProgressBlock) {
                self.downloadProgressBlock(object);
            }
        }
        else if ([object isEqual:self.uploadProgress]) {
            if (self.uploadProgressBlock) {
                self.uploadProgressBlock(object);
            }
        }
    }
    

    关于数据的处理(当然也包括对应成功失败的回调),主要是实现那几个有获取到数据的NSURLSession的代理方法,这几个代理方法是通过AFURLSessionManager来调用的,这个后续会提到。
    关于数据最终的回调都是在- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error中处理,另外比如dowmloadTask还需要处理下载中拼接数据和下载完成移动文件夹的情况。
    task完成时,除了将数据、响应、错误通过completionHandler回调出去,还发出DidComplete通知,传出去userInfo(保存着请求和响应序列化对象钝、数据、文件路径、错误信息等)。有一点特别的是completionHandler会在线程组中回调,我想这边是为了让开发者可以来设定监听。

    #pragma mark - NSURLSessionTaskDelegate
    //请求完成的回调
    //处理完成后的数据
    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
        __strong AFURLSessionManager *manager = self.manager;
    
        __block id responseObject = nil;
    
        __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
        // 把响应序列化对象保存在userInfo
        userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    
        //Performance Improvement from #2672
        NSData *data = nil;
        
        //把mutableData的值拿出来,并清空
        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;
        }
    
        // 把下载路径和下载数据保存在userInfo
        if (self.downloadFileURL) {
            userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
        } else if (data) {
            userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
        }
    
        // 如果完成的回调有错误信息
        if (error) {
            // 把错误信息也保存在userInfo
            userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
    
            //把所有完成的回调放在completionQueue的线程组中
            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;
                }
    
                // 把响应对象保存在userInfo
                if (responseObject) {
                    userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
                }
    
                // 把序列化错误信息保存在userInfo
                if (serializationError) {
                    userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
                }
    
                //把所有完成的回调放在completionQueue的线程组中
                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);
                    }
    
                    //发出任务完成的通知(userInfo附带任务中的所有信息)
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                    });
                });
            });
        }
    #pragma clang diagnostic pop
    }
    
    #pragma mark - NSURLSessionDataTaskDelegate
    //开始受到数据的回调
    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        //拼接数据
        [self.mutableData appendData:data];
    }
    
    #pragma mark - NSURLSessionDownloadTaskDelegate
    //完成下载的回调
    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        NSError *fileManagerError = nil;
        self.downloadFileURL = nil;
    
        if (self.downloadTaskDidFinishDownloading) {
            //获取用户指定的文件储存的url地址
            self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
            if (self.downloadFileURL) {
                //将储存在临时目录下的文件移动到用户指定的url地址
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
    
                //如果移动失败就发出通知(移动失败了。。。)
                if (fileManagerError) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
                }
            }
        }
    }
    

    4)AFURLSessionManager(主要类,实现功能)
    AFURLSessionManager包含一个NSURLSession属性,生成任务本质还是调用NSURLSession生成任务的方法,我们以创建dataTask为例,主要做了两个步骤,加锁通过session创建dataTask,为创建的dataTask设置代理人(AFURLSessionManagerTaskDelegate)。

    #pragma mark - 普通请求任务
    // 普通请求任务(带完成的回调)
    - (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 {
    
        __block NSURLSessionDataTask *dataTask = nil;
        
        //在串行队列中同步生成task(类似加锁)
        url_session_manager_create_task_safely(^{
            dataTask = [self.session dataTaskWithRequest:request];
        });
    
        //为生成的普通请求task添加代理
        [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    
        return dataTask;
    }
    

    我们再来看看具体怎么设置代理人,这边的代理跟我们之前的代理协议啥的不一样,这边的代理人无非是AFURLSessionManager去调用代理类的方法(比如设置进度,添加通知,甚至后面的NSURLSession的代理方法)。一个任务对应一个代理类。另外,AFURLSessionManager还会将代理人保存在字典中,key是对应task的ID。
    还有一点重要的是,为task添加通知(TaskDidResume和DidSuspend),这样就会剔除其他外来的任务,让该类产生的任务发出对应的通知。

    //设置普通请求task的代理人
    - (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 *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
    
        dataTask.taskDescription = self.taskDescriptionForSessionTasks;
        //为task设置代理(把dataTask传给delegate)
        [self setDelegate:delegate forTask:dataTask];
    
        delegate.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    
    //为task设置代理
    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        // 断言判断参数是否为空
        NSParameterAssert(task);
        NSParameterAssert(delegate);
    
        [self.lock lock];
        // 以task的id为key,AFURLSessionManagerTaskDelegate为value储存起来
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
        // 代理人为task设置进度
        [delegate setupProgressForTask:task];
        //给task添加恢复和暂停的通知(AFNSURLSessionTaskDidResumeNotification和AFNSURLSessionTaskDidSuspendNotification)
        [self addNotificationObserverForTask:task];
        [self.lock unlock];
    }
    

    那什么时候移除代理呢?那就是任务完成的时候。在这边我们也可以看到task完成的代理方法执行时,主动去获取对应task的代理,让他去执行对应操作。

    //task完成时调用
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
        // 当在后台完成任务时,委托可能是nil
        if (delegate) {
            // 调用代理对应的方法(让代理去处理)
            [delegate URLSession:session task:task didCompleteWithError:error];
            // task完成了,移除代理
            [self removeDelegateForTask:task];
        }
    
        if (self.taskDidComplete) {
            self.taskDidComplete(session, task, error);
        }
    }
    

    前面我们只是讲了生成dataTask,对应的还有downloadTask,uploadTask步骤也差不多(NSURLSession生成task,添加代理)。既然生成task,也遵守了其协议,肯定实现了NSURLSession的代理方法,就如上面提到的AFURLSessionManager提供了对NSURLSession协议的自定义处理的Block(类似于代理)。
    这个意思是在代理方法中你可以选择默认的处理或自定义处理(实现自定义处理的Block)。以收到dataTask收到数据的代理方法为例,默认的处理是让代理类去处理,进行数据的拼接,我们也可以实现dataTaskDidReceiveData,自己处理(这种如果用代理实现应该好理解一点)。

    //dataTask收到数据时调用
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
    
        //调用代理对应的方法(让代理去处理)
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        [delegate URLSession:session dataTask:dataTask didReceiveData:data];
    
        //自己处理
        if (self.dataTaskDidReceiveData) {
            self.dataTaskDidReceiveData(session, dataTask, data);
        }
    }
    

    关于上面提到的(2)和(3)功能都说完了,我们来说说第一个功能(1)获取正在执行的所有任务、请求任务、上传任务、下载任务。其实就是使用session的getTasksWithCompletionHandler方法。

    #pragma mark -  获取当前运行的任务
    - (NSArray *)tasksForKeyPath:(NSString *)keyPath {
        __block NSArray *tasks = nil;
        // 创建一个信号量,值为0
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            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))]) {
                tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
            }
    
            //信号量加1
            dispatch_semaphore_signal(semaphore);
        }];
    
        //一直等待,知道信号量不为0才会执行到这一步
        //将计数值减1,并且执行返回
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
        return tasks;
    }
    
    // 获取当前正在运行的tasks
    - (NSArray *)tasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    // 获取当前正在运行的dataTasks
    - (NSArray *)dataTasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    // 获取当前正在运行的uploadTasks
    - (NSArray *)uploadTasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    // 获取当前正在运行的downloadTasks
    - (NSArray *)downloadTasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    

    相关文章

      网友评论

        本文标题:【源码解读】AFNetWorking ─── 网络请求

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