AFNetworking是目前Apple开发中使用最广泛的网络库(Swift版本Alamofire);
前言
这篇文章以部分AFNetworking源码为基础,解析这个网络库的内部组成和部分关键技术;通过这篇文章可以加深一点对AFNetworking的整体组成结构、和网络请求关键部分的理解;框架中涉及到的UI扩展和其他部分的内容封装主要会以说明其作用的方式进行概述,并给出一些详细讲述的文章链接方便更具体的了解细节;
正文内容
文章分为以下两个部分:
- AFNetworking的组成结构
- AFNetworking核心部分的实现
1、AFNetworking的组成结构
AFNetworking是一个轻量级的网络库,它的组成可以通过下面这张图来概括:
可以大致把框架划分为5个部分,上图中红线线框标注的部分为核心部分,下面分别对这几个部分进行说明;
a、AFHTTPSessionManager
AFHTTPSessionManager继承自AFURLSessionManager,它的作用主要是封装了不同的HTTP请求方式(GET、HEAD、POST、PUT、PATCH、DELETE)的便捷方法;我们在实现自己APP的网络层时一般都是通过生成AFHTTPSessionManager的单例对象进行具体的网络请求任务的;
AFHTTPSessionManager中不同的请求方法都会通过调用本类中的以下方法生成具体的请求任务,并在返回task后调用task的resume方法开始这个任务:
//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;
//根据请求方法等参数生成具体的请求URLRequest;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) { //生成请求request出错时才执行;self.completionQueue是任务完成时的回调队列,如果没有设置则使用主线程队列
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
//通过调用父类AFURLSessionManager的方法生成具体的请求任务,并根据任务的完成状态执行对应的回调block
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
//completionHandler会在任务请求结束时回调,请求可能成功或失败
if (error) {
if (failure) { //执行失败的回调block
failure(dataTask, error);
}
} else {
if (success) { //执行成功的回调block
success(dataTask, responseObject);
}
}
}];
return dataTask; //返回具体的网络任务,并会在调用方执行任务的resume方法
}
b、核心部分(AFURLSessionManager、AFURLRequestSerialization、AFURLResponseSerialization)
这部分是网络库的核心部分,一个请求从开始到完成主要是由AFURLSessionManager、AFURLRequestSerialization、AFURLResponseSerialization这三部分协作完成的;
- AFURLSessionManager负责网络任务的创建和管理,并通过设置AFURLSessionManager为self.session的代理对象,负责实现网络任务的代理回调方法;
- self.requestSerializer会通过各种请求信息(包括请求方法、头部信息、请求参数等)生成一个请求使用的NSMutableURLRequest实例;
- self.responseSerializer会在请求完成时,对返回的数据进行验证和解析,并交付解析后的格式化数据;
c、AFNetworkReachabilityManager
网络连接状态发生变化的监听类,同时提供block形式的接口设置网络状态发生变化时的回调;并会在网络连接状态发生改变时发出对应的通知,因此如果我们APP的网络层有需要监听网络变化的需求时,可以通过监听这个类发出的通知来实现;
关键函数包括以下两个:
//开始监听网络连接状态
- (void)startMonitoring {
、、、、
/**核心代码,调用SystemConfiguration.framework框架中的方法,
增加主线程Runloop的commonModes下self.networkReachability对象的网络状态变化监听 */
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
、、、、
}
//结束监听的方法
- (void)stopMonitoring {
、、、、
/**核心代码,调用SystemConfiguration.framework框架中的方法, 取消在主线程Runloop的commonModes下面self.networkReachability的网络状态监听*/
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
d、AFSecurityPolicy
HTTPS的支持类,主要作用是在iOS系统验证服务端返回的证书之前,在类中做一步提前验证;如果在AFSecurityPolicy中的验证不通过,则直接取消请求,验证通过之后在走系统的认证;如果在AFSecurityPolicy中没有设置具体的安全验证策略,系统会加载默认的安全策略;关于这部分的更多介绍可以看这里或者更深入的AFNetworking之于https认证;
AFSecurityPolicy中提供了三种方式校验服务器端给予的证书:
//三种校验服务器端证书的方式
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone, //代表客户端无条件地信任服务器端返回的证书(包括公信机构和自签的证书),默认安全策略使用的是这种模式
AFSSLPinningModePublicKey, //代表客户端将会使用本地保存的证书对服务器端返回的证书链中证书的PublicKey(公钥)部分进行验证,如果有一对公钥匹配成功,则验证通过;
AFSSLPinningModeCertificate, //代表客户端将会使用本地保存的证书去匹配服务器端返回的证书链,如果有一对匹配成功则验证通过;
};
e、UIKit扩展
这部分功能相对独立,先从目录结构看一下里面的内容:
在UIKit扩展中封装了图片下载和缓存功能,增加了一些系统控件的Category方法,为这些控件添加了一些便捷功能,如UIButton和UIImageView的图片下载等;要对这部分做更详细的了解可以看这里;
2、AFNetworking核心部分的原理
a、AFURLSessionManager部分
我们从AFHTTPSessionManager对象的初始化开始,看看这部分的内部实现:
//AFHTTPSessionManager的初始化最终都会统一到这个方法来完成
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
//baseURL是一个url的前缀部分,如果有这部分信息,后面调用请求方法时只要给出接口的相对路径就行;这里的作用是在baseURL后加上"/"
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url; //赋值给baseURL属性
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
这里的初始化工作主要是通过调用父类的 [super initWithSessionConfiguration:configuration];
初始化方法完成的,我们继续进入到父类的初始化方法中查看初始化过程:
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; //设置默认的SessionConfiguration对象
}
self.sessionConfiguration = configuration; //赋值给self.sessionConfiguration属性
//初始化self.session代理回调方法执行的事件队列,也就是所有的请求的代理回调方法都会在这个队列中执行
self.operationQueue = [[NSOperationQueue alloc] init
//queue的并发操作数设置为1
self.operationQueue.maxConcurrentOperationCount = 1;
//初始化网络任务的NSURLSession对象,并设置回调代理,和代理回调方法的执行队列
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer]; //初始化返回数据处理器,负责校验和转码出对应的数据格式
self.securityPolicy = [AFSecurityPolicy defaultPolicy]; //设置默认安全策略
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; //网络状态监测器,Apple Watch不需要这一对象
#endif
/** 在AFN中每个网络请求task都会关联到框架中自定义的AFURLSessionManagerTaskDelegate类的对象中;通过这个对象再去处理task的delegate回调,这个对象相当于具体task的代理;
这个字典就是用来存储AFURLSessionManagerTaskDelegate网络任务的对象的,并把task.taskIdentifier唯一标识作为key,任务对象作为value存入字典中;对这个字典的读写操作都需要加锁;
这个字典中存放了session中管理的所有task */
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init]; //操作AFURLSessionManagerTaskDelegate 词典的锁,确保词典在多线程访问时的线程安全
self.lock.name = AFURLSessionManagerLockName;
/** 以下这个方法的作用是用来异步的获取当前session的所有未完成的task;其实在初始化中调用这个方法应该一个task都没有;
这个地方之所以要调用,是为了避免应用在后台被挂起之后再回到前台,重新恢复这个session,一些之前的后台请求任务导致程序的异常crash。
关于这个问题的讨论:https://github.com/AFNetworking/AFNetworking/issues/3499 */
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
到这里初始化基本完成了,初始化过程在代码里已经增加了注释说明;初始化之后我们看一下具体的网络请求的调用过程(这里使用GET请求举例,不同的请求方法调用逻辑都一样):
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume]; //生成网络任务后,开始这个任务
return dataTask;
}
//通过调用AFHTTPSessionManager中的这个方法生成task并返回
- (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;
//根据传递进来的请求参数获取,通过self.requestSerializer生成具体的request实例,这部分的内部调用我们放在下面单独讲解
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) { //生成请求request出错时走的分支,抛出错误不继续处理这个请求
if (failure) {
//这里的self.completionQueue是设置了用来执行task完成后的回调队列,如果没有设置,则会在主线程队列中完成回调
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
//这里调用父类的方法,生成具体的task,并把请求结束后需要回调的block传递给completionHandler参数
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
//这个block会被关联给具体的task任务,当task结束后,会通过代理方法执行这个completionHandler的block,之后通过执行failure或success的block回调到我们自己的业务层代码
if (error) { //任务请求失败的回调
if (failure) {
failure(dataTask, error);
}
} else {
if (success) { //任务请求成功的回调
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
以上代码生成dataTask的过程是通过调用父类中的[self dataTaskWithRequest: uploadProgress: downloadProgress: completionHandler: ]
方法实现的;同时生成的dataTask在返回前会在父类AFURLSessionManager
中被存储和管理,看一下父类中生成dataTask的调用:
- (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;
/** 这里调用了一个作者写的C函数来安全的创建一个网络任务,主要是为了修复在iOS8.0之前的bug;
在iOS8.0之前系统通过并行队列创建任务时,可能会导致task的taskIdentifiers这个属性值不唯一,这会导致taskIdentifiers作为key关联delegate时出现错乱,最终导致错误的completionHandler被调用;
因此在这个函数的内部实现做了一个判断,在iOS8.0之前使用自己创建的serial队列同步的执行创建任务block;*/
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
/** 这一步调用非常关键,其内部的逻辑涉及到AFURLSessionManager的核心部分;
主要作用是生成delegate对象,管理这个dataTask和dataTask完成后的回调completionHandler */
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask; //返回创建好的任务给调用方
}
到这里可以看到task被生成出来并返回了,在返回task之前增加了一步很重要的调用,我们进入到 [self addDelegateForDataTask: uploadProgress: downloadProgress: completionHandler: ];
函数内部看一下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] initWithTask:dataTask];
delegate.manager = self; //建立代理对象与AFURLSessionManager对象的关联(这个属性是weak修饰的,所以不会引起循环引用)
delegate.completionHandler = completionHandler; //把回调句柄传递给代理对象对应的属性
//这个taskDescriptionForSessionTasks设置任务的taskDescription,在开始和挂起任务时,就是通过判断任务的这个值来确认它属于当前的session对象之后才Post通知的;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//这一步调用的作用是把代理对象和dataTask关联到mutableTaskDelegatesKeyedByTaskIdentifier字典中进行存储
[self setDelegate:delegate forTask:dataTask];
//设置上传和下载的回调block
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
通过这个方法调用生成了AFURLSessionManagerTaskDelegate
代理对象的实例,这个就是网络任务的自定义代理。我们请求传来的参数(completionHandler、uploadProgressBlock、downloadProgressBlock),都赋值给这个对象的属性存储起来了。我们再看看[self setDelegate:delegate forTask:dataTask];
的内部调用:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
//这里是一个系统的宏,当参数为空时,会触发上一个断言崩溃
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
/** 在加锁的情况下,把delegate代理对象存储到mutableTaskDelegatesKeyedByTaskIdentifier字典中;
这样task代理对象就被存储下来了(在AFURLSessionManager对象的mutableTaskDelegatesKeyedByTaskIdentifier属性中);
之后可以通过task.taskIdentifier从字典中获取出对应的task代理对象,方便在AFURLSessionManager中传递部分代理回调方法给代理对象进行处理
*/
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
//增加网络任务开始和挂起的通知监听;监听后在网络任务开始和挂起时都会发出对应的通知
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
到这里task任务和delegate代理对象的关联已经完成了,并且存储到了AFURLSessionManager对象的self.mutableTaskDelegatesKeyedByTaskIdentifier属性中,之后可以通过这个字典属性取出delegate对象使用,我们再看一下AFURLSessionManagerTaskDelegate代理对象内部是如何处理一个task的:
//首先是根据一个task完成初始化
- (instancetype)initWithTask:(NSURLSessionTask *)task {
self = [super init];
if (!self) {
return nil;
}
_mutableData = [NSMutableData data]; //_mutableData属性用来接收服务端返回的response数据
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; //初始化上传进度管理对象
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; //初始化下载进度管理对象
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ]) { //遍历上传和下载进度对象,对属性进行赋值
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
//这里设置 progress的一些属性,并且把两者和task的任务状态绑定在了一起;当设置progress的状态时,都会触发对应的block进行回调;
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES;
progress.pausingHandler = ^{
[weakTask suspend];
};
#if __has_warning("-Wunguarded-availability-new")
if (@available(iOS 9, macOS 10.11, *)) {
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)]) {
#endif
progress.resumingHandler = ^{
[weakTask resume];
};
}
//添加网络进度发生变化时的KVO监听,方便在进度发生变化时能实时的回调
[progress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL];
}
return self;
}
//网络进度发生变化后的KVO回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object); //执行下载进度的回调block,并把当前的进度对象传递出去
}
} else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object); //执行上传进度的回调block,并把当前的进度对象传递出去
}
}
}
在AFURLSessionManagerTaskDelegate中还实现了一些必要的网络请求代理回调方法,在看代理回调方法的内部实现前,我们先介绍一下这里的回调方法是如何调用过来的;
在配置好并返回一个task之后,会调用task的resume方法开始这个网络任务,之后网络任务的一系列代理回调方法将会被调用;
再看一下之前的初始化方法self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
;我们是把AFURLSessionManager作为代理回调对象的;也就是网络任务的所有代理回调方法都是在AFURLSessionManager中去回调和实现的,这个对象实现的代理包括以下这些:
//AFURLSessionManager实现了以下代理方法:
## `NSURLSessionDelegate`
- `URLSession:didBecomeInvalidWithError:`
- `URLSession:didReceiveChallenge:completionHandler:`
- `URLSessionDidFinishEventsForBackgroundURLSession:`
### `NSURLSessionTaskDelegate`
- `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`
- `URLSession:task:didReceiveChallenge:completionHandler:`
- `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`
- `URLSession:task:needNewBodyStream:`
- `URLSession:task:didCompleteWithError:`
### `NSURLSessionDataDelegate`
- `URLSession:dataTask:didReceiveResponse:completionHandler:`
- `URLSession:dataTask:didBecomeDownloadTask:`
- `URLSession:dataTask:didReceiveData:`
- `URLSession:dataTask:willCacheResponse:completionHandler:`
### `NSURLSessionDownloadDelegate`
- `URLSession:downloadTask:didFinishDownloadingToURL:`
- `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`
- `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`
每个代理方法都有不同的作用,会在不同的事件发生时触发回调;
这里需要补充解释的一点是,我们只在初始化方法上设置了一个delegate,系统却可以触发的四个不同代理中的回调方法;原因是这些代理之间是存在继承关系的,而在NSURLSession
实现中,只要设置了NSURLSessionDelegate这个代理,它会去判断这些所有的代理,对象是否respondsToSelector
这些代理中的方法,如果响应了就会去回调。
在回调发生时,以下几个方法的调用被转发到了AFURLSessionManagerTaskDelegate对象(网络任务代理对象)中:
//AFURLSessionManagerTaskDelegate对象实现了以下代理方法,并通过以下方法的内部实现把task对应的数据回调出去:
### `NSURLSessionTaskDelegate`
- `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`
- `URLSession:task:didCompleteWithError:`
### `NSURLSessionDataDelegate`
- `URLSession:dataTask:didReceiveData:`
### `NSURLSessionDownloadDelegate`
- `URLSession:downloadTask:didFinishDownloadingToURL:`
- `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`
- `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`
接下来我们看一部分delegate方法内部的实现,和这些delegate方法被触发调用的时机;以及那些需要转发的代理方法被转发到AFURLSessionManagerTaskDelegate代理对象中的过程,在代理对象中接收到转发的消息后又是怎么去响应处理的:
NSURLSessionDelegate代理方法
//当前这个session已经失效时,该代理方法被调用
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
/**self.sessionDidBecomeInvalid是一个私有属性,他的类型是一个typedef定义的block,在头文件里开放了这个block的set方法;通过调用set方法可以给这个属性赋值。
AFURLSessionManager中有一些列的类似属性,都可以通过调用头文件的set方法进行设置赋值;这些block属性分别对应不同的代理回调方法,设置block方便用户实现自己的回调逻辑;
如果设置了对应的block,就会在代理回调方法中触发这个block的回调执行*/
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; //发出了一个对应事件的通知,方便用户有需要时进行监听
}
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
//主要作用是在方法中完成HTTPS认证挑战
}
NSURLSessionTaskDelegate代理方法
//被服务器重定向的时调用
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
{
NSURLRequest *redirectRequest = request;
if (self.taskWillPerformHTTPRedirection) {
//如果有用户自定义的回调block,则执行这个回调block,生成重定向的request
redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
}
if (completionHandler) {
completionHandler(redirectRequest); //通过重定向的request重新请求
}
}
//当每次发送数据给服务器时,会触发这个方法回调
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
int64_t totalUnitCount = totalBytesExpectedToSend; //预期要发送的总字节数
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
//如果totalUnitCount获取失败,就使用HTTP header中的Content-Length作为totalUnitCount
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
//通过task从mutableTaskDelegatesKeyedByTaskIdentifier属性中获取出AFN 的task代理对象
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
if (delegate) { //传递事件到代理对象中
[delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
}
if (self.taskDidSendBodyData) { //如果通过set方法设置了回调block,此时就执行这个block
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
}
}
//这个方法在一个任务请求完成时调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
//通过task从mutableTaskDelegatesKeyedByTaskIdentifier属性中获取出AFN 的task代理对象
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) { //传递回调事件
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task]; //任务处理完成后就移除
}
if (self.taskDidComplete) { //set方法设置了block就回调
self.taskDidComplete(session, task, error);
}
}
以上回调方法的作用和调用时机在注释中已经说明,还里需要补充说明的是:
- 以上有两个方法都出现了把回调事件转发到delegate代理对象的调用过程,通过
[self delegateForTask:task]
方法获取出代理对象,在调用delegate对象中的对应方法,实现方法的转发;
我们知道之前task和delegate对象通过把task.taskIdentifier作为key存储到了self.mutableTaskDelegatesKeyedByTaskIdentifier字典中,因此此处可以通过task.taskIdentifier在字典中取出对应的delegate对象;并通过获取到的delegate对象进行事件的转发;
我们在看一下当任务完成时消息转发到delegate对象后的处理逻辑:
//代理对象中处理一个任务结束时的回调,通过这个方法把数据和事件回调到用户手里
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; //存储这个请求的关联数据,用于发送请求结束的通知
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy]; //这里把数据拷贝的局部变量,然后销毁掉接收存储数据的属性,减少内存开销,优化性能;
self.mutableData = nil;
}
if (self.downloadFileURL) { //如果返回的是资源地址,则把地址存入字典中
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) { //如果返回的是数据,则把数据放入字典内
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) { //当回调中包含error信息时,执行error的回调
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) { //回调到用户的block中
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]; //通过responseSerializer对象验证和序列化返回的数据
if (self.downloadFileURL) { //如果时地址数据,则把返回数据赋值成地址
responseObject = self.downloadFileURL;
}
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
//取出请求完成的回调队列,并执行回调的completionHandler把事件回调给用户
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];
});
});
});
}
}
到此我们看到了一个task任务在请求结束时,是如何通过代理对象处理返回的结果,并把结果回调到用户手中的;
NSURLSessionDataDelegate代理回调方法
//当一个任务转变成下载任务时调用
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; //获取出AFN的task代理对象
if (delegate) {
[self removeDelegateForTask:dataTask]; //把原来的任务移除
[self setDelegate:delegate forTask:downloadTask]; //把新的下载任务添加进去
}
if (self.dataTaskDidBecomeDownloadTask) { //设置了回调block,则执行这个block
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
//当接收到服务端返回的阶段性数据时调用
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; //获取出AFN的task代理对象
[delegate URLSession:session dataTask:dataTask didReceiveData:data]; //把事件传递给代理对象处理
if (self.dataTaskDidReceiveData) { //设置了block回调,则执行
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
NSURLSessionDownloadDelegate代理回调方法
//当一个下载任务完成时触发的回调方法
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; //获取出AFN的task代理对象
if (self.downloadTaskDidFinishDownloading) { //如果通过set方法设置了对应的回调block,则执行,并在执行成功后直接返回
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
if (delegate) { //如果没有通过上面的block执行完回调,则把事件传递给代理对象处理
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
//下载过程中每获取到一部分新的数据,都会触发这个方法的回调
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; //获取出AFN的task代理对象
if (delegate) { //把事件传递给代理对象处理
[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
}
if (self.downloadTaskDidWriteData) { //如果设置了这个事件的回调block,则执行对应的回调block
self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
}
//下载任务中断后重新开始时调用,会从之前的中断数据节点处继续开始下载;这个方法实现的是断点续传功能
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; //获取出AFN的task代理对象
if (delegate) { //把事件传递给代理对象处理
[delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
}
if (self.downloadTaskDidResume) { //如果设置了这个事件的回调block,则执行对应的回调block
self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
}
}
考虑到还有AFURLRequestSerialization
和AFURLResponseSerialization
部分没有讲解,限于文章的篇幅,关于delegate回调方法的讲解就到这里;我们并没有对所有的代理回调方法都进行说明,如果要了解更多的回调方法的作用,可以在源码中“option+单击”查看对应方法的官方说明;
接下来会分析构造NSURLMuatbleRequest实例的过程和对服务端返回数据的验证与序列化的过程;
b、AFURLRequestSerialization部分
AFURLSessionManager
中通过self.session
调用NSURLSession对象的系统方法(如downloadTaskWithRequest:
)生成网络任务task时,需要有一个NSURLRequest的对象作为系统方法的参数,方法内部会使用这个参数生成对应的网络任务;
AFURLRequestSerialization
的作用就是用来生成并返回NSURLRequest
类型的参数的,在AFURLRequestSerialization
内部会根据不同的请求信息生成出对应的NSURLRequest实例;我们看一下生成request实例的起始调用(以下方法只保留了创建request的代码):
- (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];
if (serializationError) {
//生成参数出错的处理
}
、、、
}
可以看到创建一个网络task的第一步,是通过调用self.requestSerializer
的方法生成request实例;我们进入到生成request方法的内部查看生成request的过程:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
//使用系统的宏断言,当参数为空时抛出异常
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString]; //通过URLString生成请求的url
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method; //设置HTTP请求方法
//遍历request请求的所有属性
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
//把设置了新值取代默认值的属性判断出来,并把属性的新值设置给mutableRequest对象
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
//对请求的参数parameters进行序列化处理,并返回处理好参数后的request对象
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
可以看到在方法内部首先通过URLString
参数创建了mutableRequest对象,然后在设置mutableRequest对象的一些参数;配合方法内的注释,以下在对两个地方做补充说明:
- 1、代码中
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths())
循环的作用;
我们先看一下循环条件中AFHTTPRequestSerializerObservedKeyPaths
函数的内部实现:
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}
这个方法的作用是获取出AFHTTPRequestSerializer
在头文件申明的所有属性(包括allowsCellularAccess、cachePolicy、HTTPShouldHandleCookies、HTTPShouldUsePipelining、networkServiceType、timeoutInterval),通过数组的形式返回;然后循环遍历这些属性;
这些属性在NSMutableURLRequest中都有对应的同名
属性,并且存在默认值,在AFHTTPRequestSerializer
中增加这些属性的申明,是方便用户通过AFHTTPRequestSerializer
对象去设置对应的属性值覆盖原有的默认值;
在循环内部通过[self.mutableObservedChangedKeyPaths containsObject:keyPath]
的进行判断,self.mutableObservedChangedKeyPaths
属性中存储了所有设置了新值的属性,因此可以判断出keyPath是否是设置了新值的;如果设置了(即条件成立),就把新的值设置给NSMutableURLRequest的对应属性覆盖掉原有的默认值;
在AFHTTPRequestSerializer
中是通过KVO的方式观察这些属性值的变化的:
//在初始化方法中,增加了这些属性的KVO监听
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; //出现新值时触发回调方法
}
}
//KVO回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { //当没有新的值时,把self.mutableObservedChangedKeyPaths中对应的属性移除掉
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else { //当属性设置了新的值时,把对应的属性添加进self.mutableObservedChangedKeyPaths中
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
- 2、生成request方法中第二个需要说明的是,在给request对象设置请求信息的过程中调用了
[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error];
方法,这是一个<AFURLRequestSerialization>
协议方法,不同请求数据的设置都需要去实现这个协议方法;
我们看一下在AFHTTPRequestSerializer
这个类中关于这个协议方法的实现:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request); //参数判空
NSMutableURLRequest *mutableRequest = [request mutableCopy];
//枚举存储在self.HTTPRequestHeaders对象中的所有请求头部信息,并设置到mutableRequest请求实例的头部信息中
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil; //接收序列化后的参数
if (parameters) { //请求参数不为空,则做参数的序列化处理
if (self.queryStringSerialization) { //如果设置了序列化参数的回调block,则通过这个block完成序列化过程
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else { //不存在用户自定义的序列化block,则通过框架提供的方式序列化参数
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters); //调用函数完成参数的序列化
break;
}
}
}
//self.HTTPMethodsEncodingParametersInURI中存储了(GET、HEAD、DELETE)三种请求方式,这里判断如果是这三种请求方式则把序列化的参数添加到请求URL上发送;如果不是这三种方式(POST、PUT等请求),则把序列化好的参数设置到http的body种进行发送;
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @""; //为空时,先设置成空字符串,避免编码时异常
}
//如果不存在指定的body编码方式,则使用application/x-www-form-urlencoded默认编码方式;这部分需要对http协议本身有一定的了解,body数据支持多种编码方式(包括json、xml、multipart/form-data等)
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; //设置Content-Type内容编码方式
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; //把数据编码成NSData类型,传递到请求body中
}
return mutableRequest;
}
以上代码的作用是把用户设置的请求header信息和序列化后的parameters参数设置到mutableRequest实例中;
其中self.HTTPRequestHeaders获取到的是用户通过调用- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
方法设置的所有请求头部信息,是一个NSDictionary类型的参数;通过枚举这个参数内部的值给request设置请求头部信息;
头部信息设置好之后,在对parameters参数做序列化处理;可以通过自定义的block做参数的序列化,具体的序列化逻辑由用户在自己的回调block中定义;还可以通过系统的默认方式进行parameters的序列化,我们看一下默认的序列化过程:
//序列化参数的方法
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
//通过调用AFQueryStringPairsFromDictionary方法生成参数的AFQueryStringPair对应数组
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
//通过pair对象的URLEncodedStringValue方法对参数做序列化
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"]; //通过&拼接数组内的元素
}
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
//往下调用
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
//负责完成参数的组装,参数内部的key-value数据都会转化成AFQueryStringPair对象
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; //接收结果的数组
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)]; //排序用的对象
//如果是dic、array、set类型就递归调用自己;不是这几种类型就通过AFQueryStringPair初始化数据并存储
if ([value isKindOfClass:[NSDictionary class]]) { //对字典类型的处理
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
以上就是完成请求参数序列化的过程;内部使用到了AFQueryStringPair对象,这个对象的定义非常简单,只有两个属性和两个方法:
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
- (instancetype)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
self.field = field;
self.value = value;
return self;
}
//对参数编码后返回
- (NSString *)URLEncodedStringValue {
if (!self.value || [self.value isEqual:[NSNull null]]) { //value值为空时的处理
//对field的值进行编码,http协议是通过ASCII 码传输的,需要对参数做编码处理
return AFPercentEscapedStringFromString([self.field description]);
} else {
//field和value都存在时,对数据做的编码处理
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
}
}
完成参数的序列化之后,会把parameters序列化后的结果设置到request中;
到此生成一个NSMutableURLRequest实例的内部调用过程就完成了,在AFURLRequestSerialization中还有几种不同的生成request实例的方式;其中Content-Type
为multipart/form-data
方式的请求,生成request的过程会更复杂一些,但是基本流程是一样的;
c、AFURLResponseSerialization部分
这部分主要用来完成对服务端返回数据的验证和序列化,把返回的NSData数据解析成对应格式的数据,包括JSON、XML和图片类型的数据;
数据的解析是从网络代理对象AFURLSessionManagerTaskDelegate实例的以下代理方法开始的(这个方法在请求完成时回调,方法内部分代码已经省略了):
//在请求完成时回调到这个方法中,在方法内部会处理请求返回的数据(这里为了方便查看,方法中只保留了解析返回数据的逻辑,和解析数据无关的代码已经过滤)
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
/**由于对返回数据的解析是一个比较耗时的操作,所以这里使用一个单独的线程队列处理数据的解析*/
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
//调用数据解析方法对数据进行解析,并得到解析的结果对象
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
//在设置好的group和queue中执行完成的回调self.completionHandler,没有设置就在默认的组和主线程中执行回调
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);
}
});
});
}
以上代码通过调用 [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]
方法可以完成返回数据的解析;
我们从[manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]
方法的申明开始,了解AFURLResponseSerialization内部的设计和数据解析的过程;
首先,responseObjectForResponse:data:error:
是一个协议方法,它的申明如下:
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
数据解析基类AFHTTPResponseSerializer
会遵循这个协议,并实现协议方法,在方法中对返回的数据做一些简单的验证(主要验证返回结果的状态码和Content-Type
的MIME数据格式);把真正的数据解析工作留给对应的子类完成;
特定类型的数据解析器(AFJSONResponseSerializer
、AFXMLParserResponseSerializer
等)都是继承自AFHTTPResponseSerializer
的,并在解析器内部去重新实现协议方法,在协议方法内完成对应格式的数据解析,并把解析后的结果数据返回;
manager.responseSerializer包括以下几种不同的数据解析器类型:
/**通过NSJSONSerialization对象把返回的数据解析成JSON格式*/
self.responseSerializer = [AFJSONResponseSerializer serializer];
/**通过NSXMLParser对象把返回的数据解析成XML格式*/
self.responseSerializer = [AFXMLParserResponseSerializer serializer];
/**通过NSXMLDocument对象把返回的数据解析成XML格式*/
self.responseSerializer = [AFXMLDocumentResponseSerializer serializer];
/**通过NSPropertyListSerialization对象把返回的数据解析成XML格式,*/
self.responseSerializer = [AFPropertyListResponseSerializer serializer];
/**把返回的数据解析成图片数据并返回UIImage*/
self.responseSerializer = [AFImageResponseSerializer serializer];
/**返回的数据格式不单一时,通过这种方式初始化*/
self.responseSerializer = [AFCompoundResponseSerializer serializer];
到这里我们大概知道了AFURLResponseSerialization关于数据解析的内部设计了:
- 在这个文件中,首先会申明一个协议,协议中申明了具体的用于解析数据的方法;
- 不同的数据解析器需要遵循这个协议,并实现特定的数据解析逻辑;
- self.responseSerializer对象用
AFHTTPResponseSerializer <AFURLResponseSerialization> *
进行申明,然后用AFHTTPResponseSerializer
的解析器子类去实例化对象,方便在调用解析方法时,能调用到对应子类实现的解析方法;
我们在看一下特定格式数据的解析过程,这里以JSON格式的解析为例(不同类型的解析过程源代码都比较相似):
#pragma mark - AFURLResponseSerialization
//在JSON解析类中实现的AFURLResponseSerialization协议方法
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
//调用父类中的方法,对返回的数据做初步验证
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
//如果验证不通过,并且当error不存在、或者是返回解析结果为空
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
//这里为了规避一个bug,简单说就是对NSJSONSerialization来说一个单独的空格" "数据输入是不合理的,所以需要把这种把这种情况排除掉;
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length == 0 || isSpace) {
return nil; //返回的结果为空时,解析后同样为nil
}
NSError *serializationError = nil;
/**调用系统方法解析json数据;不同类型的解析器的实现内部,主要不同就在这里,xml等会有不同的系统解析方法*/
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject) { //系统方法没有解析出数据,返回nil对象
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
if (self.removesKeysWithNullValues) { //如果开启了排空属性,就把解析后的内容里面的空字段过滤掉
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); //返回过滤后的结果
}
return responseObject; //返回解析后的结果
}
以上解析返回数据的内部调用都添加了注释进行说明,其中内部调用到的几个函数需要在补充说明:
- 在开始解析数据之前,先通过一个判断对数据做了一次验证;调用的方法是
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error]
,这个方法是实现在父类中的方法,每种特定数据类型的解析都会先通过这个方法对返回的数据做一个验证,验证的内容包括返回的状态码和支持的数据类型,方法的实现如下
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES; //存储验证是否通过的BOOL值
NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
/**验证返回的数据类型是否正确;JSON的解析器在初始化时设置了self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];的类型支持;这里的判断条件是服务端返回的数据类型存在,且数据不为空,但是类型却不在初始化指定的几种类型中*/
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
//此时数据类型验证不通过,如果返回有数据则需要封装返回结果的错误信息字典内容
if ([data length] > 0 && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO; //类型验证失败
}
/**对返回的状态码进行验证,状态码的初始化设置为self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; 如果返回的状态码不在初始化的这个集合中,则验证失败,可以理解为200开头的状态码都是请求成功的情况,其他情况都属于请求失败了*/
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError; //把封装好的验证错误信息赋值给error
}
return responseIsValid; //返回验证是否通过的结果
}
- 在解析数据方法内,还包括了以下几个方法的调用:
1、AFErrorWithUnderlyingError
:方法的作用是把JSON解析的错误,封装到我们需要返回给用户的error
上;
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
2、AFErrorOrUnderlyingErrorHasCodeInDomain
:方法的作用是判断是不是我们自己在验证返回数据时写入的错误信息,是的话返回YES;
我们在验证返回数据的方法里,在验证失败时写入的code和domain两个参数分别为NSURLErrorCannotDecodeContentData、AFURLResponseSerializationErrorDomain;调用这个方法时传递的code和domain同样也是这两个值;
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
//判断传递过来的域名和code码是否与error中的一致;
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); //递归调用自己判断错误信息
}
return NO;
}
3、AFJSONObjectByRemovingKeysWithNullValues
:函数的作用是在解析结果对象中移除那些值为nil或[NSNull null]
的键值对;
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
if ([JSONObject isKindOfClass:[NSArray class]]) {
//初始化用来接收移除空数据后的结果集
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
//遍历数组中的元素,递归调用移除空值的方法
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
//根据解析类型时mutable还是非mutable的,返回对应的结果集
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
//对NSDictionary类型需要移除空值字段
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
id value = (NSDictionary *)JSONObject[key];
if (!value || [value isEqual:[NSNull null]]) { //判断是否为空,为空时移除
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions); //如果不为空则递归调用遍历子集
}
}
//返回可变或不可变集合
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return JSONObject;
}
这个方法在调用前有一层判断,只有开启了移除空键值对的属性时,才会调用该方法做移除空值操作;
到此JSON数据类型的解析就讲完了,其他数据类型的解析过程基本是类似的,就不一一列举了;
最后
这篇文章主要对网络请求任务的管理、请求的生成与返回的数据解析过程做了比较深入的讲解;网络库涉及的内容本身也比较多,我们从中过滤了一些对 理解这个库原理本身影响不大的部分过程代码,包括一系列的网络代理回调方法等(关于这部分的细节可以再查看对应主题的文章);希望能加深一点你对这个库的理解。