图解AFNetworking

作者: 猹_ | 来源:发表于2017-09-28 16:22 被阅读16次

一、AFN架构的最上层是AFHTTPSessionManager

AFHTTPSessionManager继承于AFURLSessionManager,它所做的主要工作是收集HTTP请求的URL、Parameters、成功的回调、失败的回调四个参数。然后自己的属性requestSerializer根据前两个参数URL和Parameter生成一个NSMutableURLRequest类的对象。然后把它交个父类处理,父类会返回一个NSURLSessionDataTask类的对象task,然后调用[task resume]发起请求。

AFHTTPSessionManager中的处理过程
该过程对应的源码:
// AFHTTPSessionManager就是拿到这个方法产生的对象发起请求的
- (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;
    // #1:URL&Parameters --> [requestSerializer] --> Request
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }
        return nil;
    }
    __block NSURLSessionDataTask *dataTask = nil;
    // #2:Request --> [AFURLSessionManager.session] --> DataTask
    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;
}

二、AFN架构的核心类AFURLSessionManager

AFURLSessionManager就像一个“工厂”,NSURLSessionDelegate就是“流水线”,task是“原料”,responseObject是生产的“产品”。#2这一流程就是由AFURLSessionManager完成的。

它的核心属性有

  1. sessionConfiguration:会话对象的配置信息类,默认使用的是default模式;
  2. session:会话对象,用于产生任务对象task;
  3. operationQueue:处理session回调的队列,这是一个串行队列,保证了回调的按次序执行完毕(后面一个回调可能要依赖前一个的处理结果,或者前一个回调对本次的task有修改);
  4. responseSerializer:返回对象的序列化,主要负责状态判断、解析等任务;
  5. 四个xxxTasks数组:只读属性,存放的是请求任务;
  6. mutableTaskDelegatesKeyedByTaskIdentifier:以键值对的形式向task关联一些回调函数,key是taskIdentifier,value是回调组成的AFURLSessionManagerTaskDelegate类的对象;
  7. reachabilityManager:监管网络状态的对象;
  8. completionQueue:请求和解析完成后将在该队列中交付数据(调用请求的completionHandler),默认是主串行队列;
  9. completionGroup:将8所描述的操作归为该任务组,默认是一个静态的dispatch_group_t(所有的session共用)。该分组允许用户创建task1、task2...,当他们都完成时通过dispatch_group_notify()再进行后续操作。

创建的静态对象有:

  1. af_url_session_manager_creation_queue:串行队列,将session创建task的任务交付到该队列;注:此队列是修复在iOS8.0之前并行创建task时导致任务完成的回调调用异常的情况,iOS8.0系统已经修复了该bug
  2. af_url_session_manager_processing_queue:专门对返回数据进行解析的队列。这是一个并行队列,能够更高效的完成数据解析任务;
  3. af_url_session_manager_completion_group:将completionHandler归为该任务组,上述9中默认的静态任务组就是它。

#2流程细分:

step_1:创建会话session,将会话的代理指定为自己(AFURLSessionManager有相当一部分代码是session对象的代理方法),创建串行队列,指定session回调在该队列中处理;
step_2:根据Request创建任务task,同时创建AFURLSessionManagerTaskDelegate类的对象(它和task一一对应)。这个对象包含了Request对应的回调方法,它还有一个重要的任务就是存储和拼接服务器返回的数据,保存在mutableData中;
step_3:根据task的标识taskIdentifier存储#2产生的对象,保存在字典中;
step_4:当请求任务发起后,会有触发session的一系列代理方法,其中就附带了标识是哪个task的信息。当请求返回数据时根据task的taskIdentifier在step_3产生的字典中找到对应的代理,让代理拼接数据;
step_5:当请求完成时让任务的代理根据session的responseSerializer完成数据解析任务(该代理有一个对创建任务的会话的弱引用);
step_6:执行completionHandler向外抛出数据。

#2流程示意图
task和回调的存储关系
核心代码:
- (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;
    //step_2:生成task
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });
    //step_3:生成task关联的对象,将upload/downloadProgressBlock、completionHandler让关联对象管理
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    return dataTask;
}
- (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;
    [self setDelegate:delegate forTask:dataTask];//用字典构建dataTask与delegate的对应关系
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

step_1在NSURLSessionManager对象的初始化中完成。step_4是一个“循环”,不断的产生数据,它以及step_5step_6下文有详细描述。

三、NSURLSessionDelegate方法的执行以及后续任务的轮转

任务在队列中的轮转

接收数据的代理方法

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

[self delegateForTask:dataTask]就是根据dataTask的任务标识找到与它对用的代理对象,代理对象将data拼接到mutableData中保存。

请求完成时的代理方法

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    if (delegate) {
        [delegate URLSession:session task:task didCompleteWithError:error];
        [self removeDelegateForTask:task];
    }
    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

请求完成后task的代理会对mutableData做异步的解析操作,[self removeDelegateForTask:task]会移除与代理的一切通知,然后从字典中移除。

四、数据的解析和交付responseObject

先从源码看起:

- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
    __strong AFURLSessionManager *manager = self.manager;
    __block id responseObject = nil;
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];//将NSMutableData拷贝以备解析之用
        self.mutableData = nil;//释放内存空间
    }
    /* 构建通知的userInfo */
    if (!error) {
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
            /* 构建通知的userInfo */
            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];
                });
            });
        });
    }
}

贴出的源码删去了构建通知userInfo和请求异常分支的处理流程(比成功时少了一个解析操作)。第一个dispatch_async()包含了解析操作,解析完成后在主串行队列(解析完成后,用户拿到数据一般就要显示了)执行block把数据交付出去,然后在主队列发出任务完成的通知,通知的userInfo信息中AFNetworkingTaskDidCompleteSerializedResponseKey对应也是解析后的数据。

五、其他

5.1 关于AFURLSessionManagerTaskDelegate
它是NSURLSessionManager中的一个私有类,里面有两个关于上传和下载进度的属性:uploadProgress、downloadProgress,它通过把自己添加为对用task的观察者来实现自己的progress的值改变。

[task addObserver:self//自己(taskDelegate)将观察task的countOfBytesSent属性的变化情况
           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
              options:NSKeyValueObservingOptionNew
              context:NULL];
[self.uploadProgress addObserver:self//自己观察 进度(Progress)的“完成比例属性”的变化情况
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                             options:NSKeyValueObservingOptionNew
                             context:NULL];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    // 当被观察对象是任务时,更新自己的progress值
    if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
            self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
        }
    } else //当被观察对象是自己的progress时,执行上传下载的进度有更新的代理,并发进度以NSProgress对象的方式传递出去
    if ([object isEqual:self.downloadProgress]) {
        if (self.downloadProgressBlock) {
            self.downloadProgressBlock(object);
        }
    }
}
变化过程
新数据下载/上传 --> task的属性发生变化 --> taskDelegate的progress同步该变化 --> progress的完成百分比就发生变化 --> taskDelegate发出通知

5.2 关系梳理:NSURLSessionManager、NSURLSession、AFURLSessionManagerTaskDelegate、NSURLSessionTask

四者的关系
持有1:属性持有;
持有2:通过集合属性持有;
持有3:虽然NSURLSession的.h文件没有给出存储它创建的task的集合属性,但有理由相信它也是采用持有2的形式存储的。
代理1:通过直接设置代理对象的方式实现;
代理2:通过key-value的方式建立联系,然后task有一些任务(数据拼接、进度管理)要处理的时候通过该联系找打taskDelegate,让它负责完成。
5.3获取Session创建的Task
方法:- (void)getTasksWithCompletionHandler:(void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler,这是一个异步的方法,通过在Session的代理队列中执行回调的方式交付处理的结果。3个数组中包含的是有效的(未开始和进行中)task。
大胆猜测一下它的实现:
- (void)getTasksWithCompletionHandler:(void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler {
    dispatch_barrier_async(self.workQueue, ^{
        NSMutableArray *dataTasks = [NSMutableArray array];
        NSMutableArray *uploadTasks = [NSMutableArray array];
        NSMutableArray *downloadTasks = [NSMutableArray array];
        for (NSURLSessionTask *task in self.allTasks) {
            if (task.state == NSURLSessionTaskStateRunning || task.state == NSURLSessionTaskStateSuspended) {
                if ([task isKindOfClass:[NSURLSessionDataTask class]]) {
                    [dataTasks addObject:task];
                } else if ([task isKindOfClass:[NSURLSessionUploadTask class]]) {
                    [uploadTasks addObject:task];
                } else if ([task isKindOfClass:[NSURLSessionDownloadTask class]]) {
                    [downloadTasks addObject:task];
                }
            }
        }
        dispatch_async(self.delegateQueue, ^{
            if (completionHandler) {
                completionHandler(dataTasks, uploadTasks, downloadTasks);
            }
        })
    })
}

当用户通过属性获取task的时候,NSURLSessionManager用了一个信号量,等待该异步操作操作的完成,然后返回给用户。

- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    __block NSArray *tasks = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//令牌数为0
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
            tasks = dataTasks;
        }/* 删减了其他分支 */
        dispatch_semaphore_signal(semaphore);//放入一个令牌
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//令牌>0时向下执行,否者等待
    return tasks;
}

六、总结

AFNetworking框架UIKit模块提供了View层的扩展,方便使用。Serialization模块负责请求和返回数据的序列化,Security模块负责网络安全,Reachability模块负责网络环境监测,NSURLSession模块将其他模块有机的组织在一起协同工作。

相关文章

网友评论

    本文标题:图解AFNetworking

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