图解AFNetworking

一、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会话对象的指定的队列,这是一个串行队列(最大并发数是1);
  4. responseSerializer:返回对象的序列化,主要负责状态判断、解析等任务;
  5. 四个xxxTasks数组:只读属性,存放的是请求任务;
  6. mutableTaskDelegatesKeyedByTaskIdentifier:以键值对的形式向task关联一些回调函数,key是taskIdentifier,value是回调组成的AFURLSessionManagerTaskDelegate类的对象;
  7. reachabilityManager:监管网络状态的对象;
  8. completionQueue:请求并解析完成后将在该队列中交付数据(调用请求的completionHandler),默认是主串行队列;
  9. completionGroup:交付数据(调用请求的completionHandler)时指定的任务组,AFN默认是一个静态的dispatch_group_t。该分组允许用户创建task1、task2...,当他们都完成时通过dispatch_group_notify()再进行后续操作。

创建的静态对象有:

  1. af_url_session_manager_creation_queue:串行队列,将session创建task的任务交付到该队列;注:此队列是修复在iOS8.0之前并行创建task时得到的taskIdentifier异常的情况,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存储step_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模块将其他模块有机的组织在一起协同工作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容