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