AFNetworking源码阅读

AFNetworking是我们常用的网络库,我们有必要对其有必要的了解,以便进行二次封装和遇到问题时能及时的进行调试。而AF本身是对系统的NSURLSession进行的封装,所以想了解AF的整体架构,有必要先了解一下OC对于网络请求的基本姿势。

一个简单的网络请求

    NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    NSURLSession *holdSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    
    NSURLSessionDataTask * dataTask =  [holdSession dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
        NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
        NSLog(@"******%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
        if (error) {
            NSLog(@"******error:%@",[error localizedDescription]);
        }
    }];
    
    [dataTask resume];

构建一个网络请求:

第一步:先准备一个URL组建成NSRequest

第二步:根据配置信息NSURLSessionConfiguration构建一个会话NSURLSession

第三步:给NSURLSession分配一个数据请求的任务dataTaskWithRequest

第四步:异步发送请求,处理回调的数据。

看来我们用系统API发送一个网络请求还是很方便的啊!但是构建一个稳定高效的网络系统,我们还是有很多坑要填的,我们就来看下AFNetworking吧。

AFNetworking的基本架构

AFNetworking.png

右侧为非核心模块:

Security模块:为安全模块,处理https链接中的SSL相关配置。

UIKit模块:为一些系统类的拓展和辅助功能,像图片的缓存,相关网络控件网络事件的监控。

Reachability是AF自己封装的监测网络状态的模块。

左侧为网络请相关核心模块

AFHTTPRequestSerializer本事是一个协议,就一个方法。这个方法传入两个参数:NSURLRequest和URL参数字典,返回一个NSURLRequest。目的是实现对应前面栗子中NSURLRequest进行封装,主要是对于请求头和各种请求形态的编码预处理,达到http请求的要求。下面是对其包含的相关类进行一下说明。

主要有三种请求格式:
大部分的请求都是AFHTTPRequestSerializer这个来完成的也是默认的配置,另外还有AFJSONRequestSerializer和AFPropertyListRequestSerializer两种请求类型,这两者都是继承与AFHTTPRequestSerializer,主要在于前者用于特别的处理请求content-type是json文件的时候,后者用于特别的处理请求content-type是plist文件的时候。

AFQueryStringPair实现对网络请求数据进行编码并组建成键值对用=连接,编码相关问题可以来这里看。主要是内联函数AFPercentEscapedStringFromString实现了相关设置。

AFStreamingMultipartFormData主要用于文件上传时的一些设置。它其中有一个主要属性就是AFMultipartBodyStream,其继承于NSInputStream,是对读取上传资源文件流的一些设置。

AFHTTPBodyPart主要是post或put请求中的请求体相关的设置

AFURLResponseSerialization类本身也是一个协议,该方法传入一个response和一个data返回指定类型的对象。这个协议用于出来网络请求返回的数据response,对数据进行验证、解码,并根据MIME类型返回正确的数据。

AFHTTPResponseSerializer下面其他几个类型的基类,用于验证返回的数据是否正确,确认解码方式,code码等。

AFJSONResponseSerializer处理application/json、text/json、text/javascript数据,我们最常用的出具处理方式,返回序列化后的id对象。

AFXMLParserResponseSerializer和AFXMLDocumentResponseSerializer处理application/xml、text/xml数据。前者生成NSXMLParser对象,后者生成NSXMLDocument对象。

AFPropertyListResponseSerializer处理application/x-plist数据

AFImageResponseSerializer处理image这个主类型下所有类型

AFCompoundResponseSerializer应对多种处理类型,自行判断可以处理的类型。

AFHTTPSessionManager继承与AFURLSessionManager,针对HTTP请求的各种请求类型进行了封装,主要目的是对外提供便于使用的接口,这里实现了栗子中,NSURLRequest的准备,拿到对应的task,并对task进行启动。

AFURLSessionManager核心类,实现对task请求的hook,准备session,监控session的各种代理状态,监控task的各种代理状态,并利用AFURLResponseSerialization进行数据的解析与回调。

NSURLSessionConfiguration是比较重要的配置信息类,三个初始化方法:

  • +defaultSessionConfiguration返回标准配置,共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage;
  • +ephemeralSessionConfiguration返回一个预设配置,没有持久性存储的缓存,Cookie或证书;+
  • +backgroundSessionConfiguration:它会创建一个后台session,它以在应用程序挂起,退出,甚至崩溃的情况下运行上传和下载任务。

_AFURLSessionTaskSwizzling实现对resume、suspend的hook,并发送通知。

AFURLSessionManagerTaskDelegate实现对task的上传、下载进度的回调,获取数据,获取到完整数据后进行回调。

AFNetworking的线程相关问题

网络的重点在于数据的高并发处理,所以有必要研究一下AF在线程方面所做的努力。

 self.operationQueue = [[NSOperationQueue alloc] init];
 self.operationQueue.maxConcurrentOperationCount = 1;
 self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

AF创建session的时候会建一个队列传进去,这个队列默认设置最大并发数只有1,第一次这个session启动一个任务的时候启动一个线程来处理这个网络请求任务,以后这个会话下的任务默认都是在这个线程中顺序执行得。

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

session每次启动一个任务,就会创建一个AFURLSessionManagerTaskDelegate的delegate对象,用于处理数据。AFHTTPSessionManager有一个字典属性mutableTaskDelegatesKeyedByTaskIdentifier用于存放以taskID为键,delegate对象为值得一组数据,这个delegate对象就是AFURLSessionManagerTaskDelegate生成的对象。此处为应对可能在不同的线程生成任务,添加了锁,防止线程竞争造成的数据异常。

 dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&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];
                });
            });
        });

此处代码是数据回来之后,对数据的处理。由于要处理的数据可能很大,所以首先开启一个异步并发队列url_session_manager_processing_queue,在子线程中处理数据,处理完成后再回调到应该去的线程。通过异步group,判断是否用户创建了group,如果没有就创建一个,同时判断用户是否创建了完成的队列completionQueue,如果没有就返回到主线程。理我可以监听自定义group,可以实现多个请求完毕后的统一处;通过设置自定义completionQueue,可以让我们的数据返回到自定义线程,一个场景就是返回的数据可能要进行大量的计算,这个时候我们没必要返回主线程,直接去自定义的子线程做这些复杂计算,待完成后再回到主线程去刷新UI。最后还有一个点就是为了保证通知在主线程,特地做了异步返回主线程去发通知。

总结:

AFNetworking面向接口编程,整体架构清爽,每个点拓展开来都有不少内容,值得我们学习,我只是对架构进行了整体的分析,希望对大家有所启发。闲来无事大家可以就感兴趣的一些模块仔细阅读,定会对整个网络请求以及线程的管理有更多的理解。

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

推荐阅读更多精彩内容