目录
简介
AFNetWorking
是使用Objective-C开发iOS程序主流的网络请求开源库。
小tip:
前面的AFURLSessionManger、AFURLSessionManagerTaskDelegate、_AFURLSessionTaskSwizzling、AFHTTPSessionManager、AFSecurityPolicy、AFNetworkReachabilityManager等可作为第一部分学习,后面的UIKit+AFNetworking可作为第二部分单独学习。
开始解读
参考大佬文章
参考大佬文章
参考大佬文章
在使用时,我们要先创建一个AFHTTPSessionManager
,调用AFHTTPSessionManager
的manager
方发来进行初始化,思考一下manager
的设计模式是什么呢? 肯定有人会说是单例模式;其实manager
所用到的设计模式是工厂设计模式。
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
- (instancetype)init {//未用到
return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {//未用到
return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
// 最终都会调用父类的初始化方法来生成对应的sessionManager
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
//为了确保NSURL +URLWithstring:relativeToURL:可以正确执行,在baseurlpath的最后添加‘/’
//url有值且没有‘/’,那么在url的未尾添加‘/’,
//如果不带‘/’会有一个默认的重定向 浪费带宽
//有没有反斜杠的意义在于该url是指向一个文件还是一个目录,说明url地址是精准表达指向的,有无'/'代表的是两个不同的地址
//没有‘/’指向的是网站目录下的一个文件
//有‘/’指向的是网站目录下的一个目录
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
// 初始化URL
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
由此可见,在AFHTTPSessionManager
中的初始化方法最终都会调用其父类的initWitchSessionConfiguration
初始化方法,返回一个sessionManager
;那么,需要去看一下父类也就是AFURLSessionManager
的初始化都做了什么:
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
// 如果会话配置为nil,对应初始化一个
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
// 初始化操作队列,并设置为串行队列 设置最大井发操作数为1
// 目的是想让并发的请求进行串行的回调
self.operationQueue = [[NSOperationQueue alloc] init];
// 1.⚠️
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
// AFJSONResponseSerializer用来序列化HTTP响应,默认json解析
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 初始化SSI所需要的 AFSecurityPolicy 用来保证请求的安全性
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
// AFNetworkReachabilityManager用来查看网络连接情况
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 2.⚠️
// 初始化可变任务字典 task 的id 作为 key,代理对象作为 value。
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// 初始化锁& 命名
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 3.⚠️
// 获取所有的task,设置一遍 delegate 用来异步的获取当前session的所有未完成的task, session和task的关系是一个session里边会有多 个task
// 初始化的时候dataTasks-一般为空,这里这么做的主要目的是为了防止从后台回来,重新初始化整个session,
// 一些之前的后台请求任务,导致程序崩溃
[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;
}
它做了这些事:
- 初始化当前的会话配置、操作队列、锁、AFNetworkReachabilityManager、AFSecurityPolicy,请求序列化以及用来存储任务的可变任务字典等属性
- 获取当前
session
中所有未完成的task
,给它们设置一遍代理
这个方法里有三点需要注意:
- 队列的最大并发操作数设置为1,这里的并发操作数值的是回调代理的线程并发数
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
是用来将每一个请求任务和自定义的AFURLSessionManagerTaskDelegate
来建立映射的;(需要深入研究,代理和这里的关系,以及利用KVO的思想实现的相关)- 在初始化的时候获取当前
session
中的所有task
,为它们重新设置一遍代理;一般来说初始化的session
中的task
应该是为空的,这里这么做的主要目的是为了防止从后台回来的时候初始化session
,对于一些后台之前的请求任务没有重设代理导致崩溃的问题;这里不同的任务调用不同的addDelegateForXXX
方法来设置代理
对于使用串行队列(并发操作数设置为1),解释是:
An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
用于计划委托调用和完成处理程序的操作队列。该队列应为串行队列,以确保回调的正确顺序。如果为 nil,会话将创建一个串行操作队列,用于执行所有委托方法调用和完成处理程序调用。
下面看一下这几个addDelegateForXXX
方法:
//为data任务添加代理
- (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
{
// 生成局部变量delegate,弱持有AFURLSessionManager对象
// 设置其manager以及completionHandler
// 由于这里是弱持有AFURLSessionManager对象,所以不会导致循环引用
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
// 设置回话任务的任务描述
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
// 调用setDelegate存储datatask
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
//为upload任务添加代理
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:uploadTask];
delegate.uploadProgressBlock = uploadProgressBlock;
}
//为download任务添加代理
- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
if (destination) {
// ⚠️
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:downloadTask];
delegate.downloadProgressBlock = downloadProgressBlock;
}
这三个方法都是为不同的任务设置代理,最终都会调用setDelegate
设置代理并存储datatask
任务:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
// 以key-value形式存储task到mutableTaskDelegatesKeyedByTaskIdentifier中
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 添加监听
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
这个方法主要是把代理和task
建立映射关系,并且存储到字典当中,同时为当前task
添加监听。在添加监听的方法中,taskDidResume
和taskDidSuspend
为接收到通知后的处理方法,用来恢复任务和暂停任务。这里,对于初始化的时候获取当前session
中的所有task
,已经为它们重新设置一遍代理。回到initWitchSessionConfiguration
方法中返回当前对象,向上返回,生成AFHTTPSessionManager *sessionManger
对象。
AFHTTPSessionManager
对象有许多的接口,我们常用的很多接口,比如:
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id<AFMultipartFormData> _Nonnull))block
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;
- (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;
.
.
.
很多我们常使用的GET、POST接口(有一些不是),底层都调用了:
- (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;
方法,下面看看实现:
- (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相关属性&参数,(requestSerializer:请求序列化器)
// 1.调用请求序列化类中的requestWithMethod方法进行序列化处理
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//序列化失败的回调处理
if (serializationError) {
if (failure) {
//completionQueue 如果设置了这个GCD的queue,那么从这个completionQueue中回调结果就好
// 否则就从主队列回调
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// NSURLSessionManager创建一个datatask
// 2.调用dataTaskWithRequest来生成一个datatask任务
__block NSURLSessionDataTask *dataTask = nil;
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;
}
先看看调用请求序列化类中的requestWithMethod
方法进行序列化处理:
通过requestSerializer
来调用requestWithMethod
对请求参数进行序列化,最终生成一个最终请求网络需要的request
实例;这里想一想:为什么要进行序列化?下面,先看看requestWithMethod
方法内部做了些什么吧。
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 判断参数是否存在 url 转化
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
// 利用url创建NSMutableURLRequest井设置http请求方法
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 循环遍历mutableRequest
// 在self.mutableObservedChangedKeyPaths根据keypath取出对应的value值,
// 存到创建的mutableRequest中
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
//通过KVC赋值
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 对mutableRequest参数做编码井且重新赋值给mutableRequest
// 调用requestBySerializingRequest方法将传入的parameters参数进行编码,井添加到request中
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
在requestWitchMethod
方法中,做了三件事情:
- 创建
mutableRequest
并设置其请求方法 - 把当前类设置的一些属性设置给
mutableRequest
- 把需要传递的参数进行编码并且设置到
mutableRequest
当中
下面看一下编码的代理方法:
#pragma mark - AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 从自己持有的HTTPRequestHeaders中遍历,如果有值,就设置给mutableRequest的head
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
//把网络请求的参数转换为NSString类型
NSString *query = nil;
if (parameters) {
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// 判断该request中是否包含了GET、HEAD、 DELETE(都包含在HTTPMethodsEncodingParametersInURI)
// 因为这几个method的quey是拼接到url后面的,而POST、PUT是把query拼接到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 = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
// 用于序列化参数的字符串编码,默认“NSUTF8StringEncoding”
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
- 从当前请求队列中拿到
self.HTTPRequestHeaders
中拿到设置的参数,赋值要请求的request
中 - 把网络请求的参数转换为
NSString
类型,这一步是对参数进行转码 - 将请求方法拼接到
url
中;GET、HEAD、DELETE这几个method
的quey
是拼接到url
后面的。而POST、PUT是把query
拼接到http body
中的
以下这几个方法就是我们在转码的时候所使用的方法,如何将请求参数进行转码:也就是通过递归调用并解析,直到解析除了array,dict,set以外的元素,然后将最终得到的参数返回:
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
// 把参数给AFQueryStringPairsFromDictionary,拿到AF的一个类型的数据
// 一个key,value对象,在URLEncodedStringValue拼接keyValue
// 加到数组里
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
// 拆分数组返回参数字符串
return [mutablePairs componentsJoinedByString:@"&"];
}
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
// 递归调用并解析key-value,直到解析的除了array,dict,set以外的元素,然后把最终得到的参数返回
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
// 根据需要排列的对象的description来进行升序排列,并且selector使用的是compare
// 因为description返回NSString,所以此处用NSString的compare函数
// 如:@[@"foo", @"bar", @"bae"] ----> @[@"bae", @"bar", @"foo"]
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
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;
}
到这里,序列化的部分结束,返回一个NSMutableURLRequest
实例;紧接着设置请求头并且通过回调处理序列化失败的情况。
那么,为什么要进行序列化呢?
在http网络请求是基于字节流的网络传输,序列化是可以将一个对象转化成一段字节编码,以此方便在网络上传输或者做其他存储处理,使用的时候在对其做反序列化;简单的来说就是为了统一,我们可以用自己的方法来保存对象,但是在底层只提供一种保存对象状态的机制。因此我们在存储的时候需要进行序列化,以此来和提供的保持一致,在读取的时候对其进行反序列化,得到我们想要的形式。
再看回前面,看一下如何调用dataTaskWithRequest
来生成一个datatask
任务,在调用的时候,将序列化后的request
,等参数传入,先看看dataTaskWithRequest
方法里边做了什么事情:
- (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;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
// 设置其delegate,包括进度等
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
可以看到,生成了一个datatask
,并且调用addDelegateForDataTask
方法来设置代理,设置代理这一块,和在初始化的时候为当前初始化session
中的task
设置代理的过程是一样的(见上面⬆️)。
最终返回一个dataTask
任务,创建完成之后通过回调返回进度以及comple
情况,在dataTaskWithHTTPMethod
方法中进行处理;这里回到dataTaskWithHTTPMethod
方法中,做完上述处理之后,最终将生成的dataTask
返回到GET方法当中,这样最终在GET中就拿到了我们可以发送请求的task
,最后调用系统方法[dataTask resume];
发起网络请求。
在GET方法中有progress,success,failure回调,使用的时候直接根据各个块的回调来处理不同情况,不用像原生的方法那样,进行多次判断,确认哪一种情况,再去做处理。
回到我们的初始化方法,在调用AFURLSessionManager
的initWithSessionConfiguration
方法初始化一个sessionManager
的时候,使用下面的方法:
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
由此看到,在初始化session
的时候,将AFURLSessionManager
作为了所有task
的delegate
。因此当我们进行网络请求的时候,这些代理就会被执行。AFURLSessionManager
遵守的了包括:NSURLSessionDelegate
、NSURLSessionTaskDelegate
、NSURLSessionDataDelegate
、NSURLSessionDownloadDelegate
、NSSecureCoding
、NSCopying
协议。
前面说过了,一些接口是调用dataTaskWithHTTPMethod
方法实现的,下面看看上传文件的接口:
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
看看实现:
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
可以看到,这里面NSURLSessionDataTask
的初始化使用了uploadTaskWithStreamedRequest
,一会儿下面会对这个进行讲解。
前面了解了大致流程,接下来来看看网络通信模块(AFURLSessionManger
,AFHTTPSessionManager
)。
首先AFURLSessionManger
是AFHTTPSessionManager
的父类,其中AFURLSessionManger
内部还包含AFURLSessionManagerTaskDelegate
和_AFURLSessionTaskSwizzling
。
AFHTTPSessionManager
本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager
或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能。
核心类
AFURLSessionManger
作用
AFURLSessionManager
负责生成对应的NSURLSession
的实例,管理AFNetworkReachabilityManager
和AFSecurityPolicy
,以此一来查看网络的连接情况,二来保证请求的安全,同时初始化生成一个AFJSONResponseSerializer
的实例来序列化HTTP的响应结果。
属性和接口方法
核心的方法包括:初始化方法,针对不同任务的request
方法。初始化方法的实现在前面已经详细讲过,最终生成一个AFURLSessionManager
的实例对象;前面也顺带介绍了dataTaskWithRequest
。下面来介绍一下其他的几个方法:
// 使用本地文件的指定请求创建一个NSURLSessionUploadTask
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
url_session_manager_create_task_safely(^{
// 根据request以及要上传的本地文件的URL创建一个uploadtask
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
// uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113)
if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
}
}
});
// 设置代理
if (uploadTask) {
[self addDelegateForUploadTask:uploadTask
progress:uploadProgressBlock
completionHandler:completionHandler];
}
return uploadTask;
}
// 使用指定的HTTP body请求创建NSURLSessionUploadTask
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
url_session_manager_create_task_safely(^{
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
});
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
// 使用指定的流式处理请求创建NSURLSessionUploadTask
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
url_session_manager_create_task_safely(^{
uploadTask = [self.session uploadTaskWithStreamedRequest:request];
});
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
uploadTaskWithRequest
根据不同的数据创建一个NSURLSessionUploadTask
任务,最终都会走到addDelegateForUploadTask
为对应的uploadTask
设置代理,设置代理的方法前面讲解过了。
// 使用指定的请求创建NSURLSessionUploadTask
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
// 使用指定的请求request来创建对应的NSURLSessionDownloadTask,并且为所创建的task设置代理,最终将其返回
// 创建重用数据的下载任务:使用已经下载的部分数据 resumeData 创建一个下载任务,继续进行下载
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithResumeData:resumeData];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
// 使用已经下载的部分数据 resumeData 创建一个下载任务,继续进行下载。并且为所创建的task设置代理,最终将其返回
uploadProgressForTask
和downloadProgressForTask
这两个方法是用来获取上传或者下载的进度;另外还有一些自定义的block
的set
方法。关于这一点,里边使用到的一些自定义的block
回调,作者在.m文件中声明一些block
属性,并且复写了其set
方法,然后又在.h文件中声明这些set方法:这样做的目的看来是为了使用方便,我们在调用set
方法设置这些block
,能够很清晰的看到block
的各个参数与返回值。
代理
前面提到AFURLSessionManager
遵守的代理有NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying
,也对应实现了这些代理中的一些方法:
遵守的这些代理,最终AFURLSessionManager
对这些代理做了一些公共的处理,最终转发到自定义的代理AFURLSessionManagerTaskDelegate
的3个代理方法中,用来负责把每个task
对应的数据回调回去。在AFURLSessionManager
里边重要的代理包括NSURLSessionTaskDelegate
,NSURLSessionDataDelegate
以及NSURLSessionDownloadDelegate
,这里重点是看源码的实现,以下是AFURLSessionManager
实现的NSURLSessionDownloadDelegate
代理。
在AFURLSessionManager
中实现了NSURLSessionDownloadDelegate
的三个代理方法,分别如下:
#pragma mark - NSURLSessionDownloadDelegate
//下载完成时调用
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
//转发代理
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (self.downloadTaskDidFinishDownloading) {
// 调用自定义的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) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
//周期性地通知下截进度调用
//bytesWriten:自上次调用该方法后,接收到的数据字节数;
//totalBytesWritten:目前已经接收到的数据字节数数;
//totalBytesExpectedToWrite:期望收到的文件总字节数,是由Content-Lengthheader提供。如果没有提供,默认是NSURLSessionTransforSizeUnknown
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
}
if (self.downloadTaskDidWriteData) {
self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
}
//当下载被取消或者失败后重新恢复下载时调用告诉代理,下载任务重新开始下载了。
//didResumeAtOlfset:在偏移恢复 从哪里开始恢复下载的偏移
//expectedTotaiBytes:预期的总字节数
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
}
if (self.downloadTaskDidResume) {
self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
}
}
这三个代理方法分别用来对下载任务进行处理,依次是下载完成时的调用,周期性通知下载进度的调用,当下载被取消或者失败后重新恢复下载时的调用;这三个代理方法最终都会进行代理转发,到AFURLSessionManagerTaskDelegate
中,AF中的deleagate
是需要对应每个task
去私有化处理的。对应看看AFURLSessionManagerTaskDelegate
中的这三个代理方法都做了什么。
#pragma mark - NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
// 更新当前下载进度
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
self.downloadProgress.completedUnitCount = totalBytesWritten;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
// 更新当前下载进度
self.downloadProgress.totalUnitCount = expectedTotalBytes;
self.downloadProgress.completedUnitCount = fileOffset;
}
// 下载完成时调用
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
// 得到下载的文件路径
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
NSError *fileManagerError = nil;
//把下载路径移动到我们自定义的下载路径
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
在下载完成时的调用代理方法中,AFURLSessionManager
和AFURLSessionManagerTaskDelegate
中都进行了文件路径的移动,而NSURlSession
代理的下载路径是所有request
公用的下载路径,设置之后所有的request
都会下载到之前的那个路径。而AFURLSessionManagerTaskDelegate
中对应到每一个task
中,每一个task
可以设置自己的下载路径。
总结:这些代理方法在AFURLSessionManager
中实现的时候都是对session
做一个公共的处理,每一个不同的task
进行特定的处理时,需要将代理转发到AFURLSessionManagerTaskDelegate
中,在AFURLSessionManagerTaskDelegate
的代理中实现。
AFURLSessionManagerTaskDelegate
主要用来管理进度,并且在task结束的时候回调使用。在上述提到AFURLSessionManagerTaskDelegate
中关于NSURLSessionDownloadDelegate
的代理方法实现,对应到每一个task
中,每一个task
可以设置自己的下载路径;相应的也实现了NSURLSessionDataDelegate
、NSURLSessionTaskDelegate
。这些代理都是用来对当前特定的task
做处理;
监听的处理方法,observeValueForKeyPath
,这个方法是用来当datatask
状态发生改变时的监控处理逻辑,调用block
回调,用户拿到进度。
_AFURLSessionTaskSwizzling
用来修改NSURLSession
的resume
和suspend
方法,使用af_resume
和af_suspend
这两种方法来替换原有的resume
和suspend
方法;这样做是为了在方法resume
或者suspend
被调用时发出通知;
load
方法中采用OC中Runtime
的method swizzling
来进行实现, AFNetworkingTaskDidResumeNotification
来通知当前的任务状态为resume
,那么就需要调用taskDidResume:
函数,而想要调用taskDidResume:
函数就得调用af_resume
函数。同理,AFNetworkingTaskDidSuspendNotification
来通知当前的任务状态为suspend
,那么就需要调用taskDidSuspend:
函数,而想要调用taskDidSuspend:
函数就得调用af_suspend
函数。
下面看一下load
方法和swizzleResumeAndSuspendMethodForClass
方法:
+ (void)load {
/**
WARNING: Trouble Ahead
https://github.com/AFNetworking/AFNetworking/pull/2702
*/
if (NSClassFromString(@"NSURLSessionTask")) {
// 创建一个session的配置对象
// 利用它创建一个session,进一步创建task
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
//originalAFResumeIMP:af_resume方法的实现
//class_getInstanceMethod:返回给指定类的实例方法
//method_getImplementation:返回方法的实现
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
//循环去currentClass中查看是否有resume方法
while (class_getInstanceMethod(currentClass, @selector(resume))) {
Class superClass = [currentClass superclass];
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
//判断af_resume和当前类的父类的resume的实现不相等并且原来的af_resume和当前类的resume实现不相等
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
//调用swizzleResumeAndSuspendMethodForClass调剂该类的resume和suspend方法
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
}
//调剂theClass的resume和suspend方法
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
}
if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
}
}
//af_swizzleSelector()函数实现
static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
下面看一下af_resume
和af_suspend
的实现。
- (void)af_resume {
//断言是否状态恢复
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
//获取取消状态
NSURLSessionTaskState state = [self state];
[self af_resume];
//如果状态是取消状态,通过注册的AFNSURLSessionTaskDidResumeNotification值,通知给self(接收通知的对象)
if (state != NSURLSessionTaskStateRunning) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
}
- (void)af_suspend {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_suspend];
if (state != NSURLSessionTaskStateSuspended) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
}
}
AFHTTPSessionManager
作用
AFHTTPSessionManager
本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager
或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能。
来看看AFHTTPSessionManager
的一些方法的实现,初始化方法最终都会调用到AFURLSessionManager
中的初始化方法完成sessionManager
的初始化。当以某一种方式发送一个网络请求的时候,以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
{
// 调用dataTaskWithHTTPMethod方法生成一个dataTask任务
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
//调用resume 开始请求
[dataTask resume];
return dataTask;
}
里面做了两件事情:生成一个dataTask
任务、调用resume
开启请求。该类的一个核心方法:dataTaskWithHTTPMethod
,使用自定义HTTPMethod
请求创建NSURLSessionDataTask
。前面已经看过了,再看一遍:
- (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相关属性&参数,(requestSerializer:请求序列化器)
// 1.调用请求序列化类中的requestWithMethod方法进行序列化处理
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//序列化失败的回调处理
if (serializationError) {
if (failure) {
//completionQueue 如果设置了这个GCD的queue,那么从这个completionQueue中回调结果就好
// 否则就从主队列回调
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// NSURLSessionManager创建一个datatask
// 调用dataTaskWithRequest来生成一个datatask任务
__block NSURLSessionDataTask *dataTask = nil;
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;
}
该方法做了两件事情:对请求参数进行序列化、调用dataTaskWithRequest
方法生成后一个datatask
任务;最终返回一个datatask
。
网络通讯模块所做的就到此为止,该模块主要的任务就是发起网络请求。分成AFURLSessionManger
和 AFHTTPSessionManager
两部分来做处理。 AFURLSessionManger
是AFHTTPSessionManager
的父类,其中AFURLSessionManger
内部还包含AFURLSessionManagerTaskDelegate
和_AFURLSessionTaskSwizzling
;AFHTTPSessionManager
本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager
或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能。
在AFURLSessionManager
里有这么两条属性:
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
对应初始化方法:
// 初始化SSI所需要的 AFSecurityPolicy 用来保证请求的安全性
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
// AFNetworkReachabilityManager用来查看网络连接情况
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
接下来就来解析一下AFSecurityPolicy
和AFNetworkReachabilityManager
。
AFSecurityPolicy
作用
网络通信安全策略模块AFSecurityPolicy
,AFSecurityPolicy
提供了https请求时的网络安全策略,同时在AFNetworking
中,使得在系统底层自己去验证之前,AFNetworking
可以先去验证服务端的证书,如果验证不通过,则直接越过系统的验证,取消https的网络请求。否则,继续去走系统根证书的验证。
属性和接口方法
在AFURLSessionManager
中的NSURLSession
的相关代理中,实现了关于https验证的代理方法:
//NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
//NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.taskDidReceiveAuthenticationChallenge) {
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
在服务端接收到客户端发来的网络请求的时候,有时候需要先验证客户端是否为正常的用户,再去依次为客户端返回数据。这一过程可以参考HTTPS的请求流程:
上面这两个代理方法是用来做https证书验证的,NSURLSessionDelegate
的验证级别是在session
级别上,就是在有一个sessionManager
的时候,会走到这里的代理方法,来去进行证书验证;这个验证是seesion
级别的,也就是当前session
证书验证通过的话,session
所管理的task
也都遵守该证书。而NSURLSessionTaskDelegate
这个的验证级别是在task
级别,这里多了一个参数task
,然后调用我们自定义的Block会多回传这个task
作为参数,这样我们就可以根据每个task
去自定义我们需要的https认证方式。
接下来看看AFSecurityPolicy
内部是如何来满足各种https认证的需求的。
在AFSecurityPolicy
中提供了三种初始化的方法:
// 默认指定了SSLPinningMode模式为AFSSLPinningModeNone
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
}
// 创建井返回具有指定固定模式的安全策略。
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
// 创建并返回具有指定固定模式的安全策路。withPinnedCertficates:要锁定的证书
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = pinningMode;
[securityPolicy setPinnedCertificates:pinnedCertificates];
return securityPolicy;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.validatesDomainName = YES;
return self;
}
检验是否应根据安全策略接受指定的服务器信任的,核心方法是以下evaluateServertTrust:
这个方法,我们一起看看AFSecurityPolicy
是如何实现的:
// 是否应根据安全策略接受指定的服务器信任
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
// allowInvalidCertificates是否公许无效证书《也就是自建的证书)默认为NO
// validatesDomainName是否需要验证城名
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
//不受信任,返回
return NO;
}
//用来存储验证策略
NSMutableArray *policies = [NSMutableArray array];
//validatesDomainName:是否验证证书的CN字段中的域名。默认为“是”进行域名验证
if (self.validatesDomainName) {
//如果需要验证domain,那么就使用SecPolicyCreateSSL函数创建验证策略,其中第一个参数为true表示验证整个SSL证书链,
//第二个参数传入domain,用于判断整个证书链上叶子节点表示的那个domain是否和此处传入domain一致添加验证策略
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
//如果不需要验证domain,就使用默认的BasicX509验证策略
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
//serverTrust:X,509服务器的证书信任。为serverTrust设置验证策略,即告诉客户端如何验证serverTrust
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
//有验证策略了,可以去验证了。如果是AFSSLPinningModeNone,是自签名,直接返回可信任,否则不是自签名的就去系统根证书里去找是否有匹配的证书。
if (self.SSLPinningMode == AFSSLPinningModeNone) {
//如果支持自签名,直接返回YES,不允许才去判断第二个条件,判断serverTrust是否有效
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
//如果验证无效AFServerTrustIsValid,而且alowInvalidCertificates不允许自签,返回NO
return NO;
}
// 判断SSLPinningMode
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
//验证证书类型
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
// 把证书data,用系统api转成SecCertificateRef类型的数据 SecCertificateCreatewithData函数对原先的pinnedCertificates做一些处理,保证返回的证书都是DER编码的X.589证书
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
// 将pinnedCertificates设置成需要参与验证的AnchorCertificates
//(银点证书,通过SecTrustSetAnchorCertificates设置了参与校验销点证书之后,
//假如验证的数字证书是这个错点证书的子节点,即验证的数字证书是由错点证书对应CA或子CA签发的,
//或是该还书本身,则信任该证书),具体就是用SecTrustEvaluate来验证。 serverTrust是服务器来的验证,有需要被验证的证书。
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
//再去调用之前的serverTrust去验证该证书是否有效,有可能:经过这个方法过滤后,serverTrust里面的pinnedCertificates被筛选到只有信任的那一个证书
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
//注意,这个方法和我们之前的锚点证书没关系了,是去从我们需要被验证的服务端证书,去拿证书链。
//服务器端的证书链,注意此处返回的证书链顺序是从叶节点到根节点
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
//如果我们的证书中,有一个和它证书链中的证书匹配的,就返回YES
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
// 公钥验证AFSSLPinningModePublicKey模式同样是用证书绑定(SSL Pinning)方式验证,客户端要有服务端的证书考贝,
// 只是验证时只验证证书里的公销,不验证证书的有效期等信息。只要公销是正确的,就能保证通信不会被窃听,
// 因为中间人没有私钥,无法解开通过公钥加密的数据。
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
//从serverTrust中取出服务器端传过来的所有可用的证书,并依次得到相应的公钥
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
//遍历服务端公钥
for (id trustChainPublicKey in publicKeys) {
//遍历本地公钥
for (id pinnedPublicKey in self.pinnedPublicKeys) {
//判断如果相同 trustedPublicKeyCount+1
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
总结下,该方法做了以下的事情:
- 先去判断“是否有域名,且允许自建证书,需要验证域名(According to the docs, you should only trust your provided certs for evaluation.根据这些文件,你应该只相信你提供的证书进行评估。固定证书被添加到信任中。没有固定凭证,就没有什么可评估的。直接返回)
- 不满足上述条件时开始做验证:
(1)判断验证策略
(2)验证https验证模式,默认是无,还有证书匹配和公钥匹配
·验证证书类型;setverTrust
中去获取证书链,然后和我们一开始初始化设置的证书集合self.pinnedCertificates
去匹配,如果有一对能匹配成功的,就返回YES,否则NO。
·公钥验证:公钥验证从setverTrust
,获取证书链每一个证书的公钥,放到数组中。和我们的self.pinnedPublicKeys
去配对,如果有一个相同的,就返回YES,否则NO。至于这个self.pinnedPublicKeys
,初始化的地方如下:
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
//创建公钥集合
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
//从证书中拿到公钥
for (NSData *certificate in self.pinnedCertificates) {
//AFPublicKeyForCertificate判断serverTrust是否有效
id publicKey = AFPublicKeyForCertificate(certificate);
if (!publicKey) {
continue;
}
[mutablePinnedPublicKeys addObject:publicKey];
}
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
AFSecurityPolicy
对于证书验证提供了以上的方法,在有特殊验证需求的时候可以使用AF提供的验证需求,在这里,如果进入AF的验证,AF会让在系统验证证书之前,先进行自主验证,如果自主验证不通过,就会直接取消网络请求;如果通过则继续进行系统验证。
AFNetworkReachabilityManager
作用
AFNetworkReachabilityManager
的主要功能就是进行网络检测,也可以通过设置状态改变回调来获取当前网络状态,也能够调用start
和stop
方法开启和停止网络监控。
属性和接口方法
核心的方法是startMonitoring
和stopMonitoring
,我们具体来看看该类中是如何实现开始网络监测和停止网络监测的。
- (void)startMonitoring {
//停止之前的监听
[self stopMonitoring];
//networkReachability表示的是创建测试连接返回的引用
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
//根据网络状态status来设置网络状态监听的回调block
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
/*
typedef struct {
CFIndex version; 传递的结构类型的版本号,作为SCDynamicStore创建函数的参数。此结构是版本 0。
void * __nullable info;指向用户指定的数据块的指针。
const void * __nonnull (* __nullable retain)(const void *info);
保留用于为信息字段添加保留的回调。如果此参数不是指向正确函数的指针原型,行为未定义。该值可能为空
void (* __nullable release)(const void *info);
释放用于删除以前添加的保留项的回调。对于信息字段。 如果此参数不是指向函数的正确原型,行为未定义。该值可能为 NULL。
CFStringRef __nonnull (* __nullable copyDescription)(const void *info);
copyDescription用于提供信息字段。
} SCNetworkReachabilityContext;
*/
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
//给客户端指定对应target(该参数和需要检测网络狀况的地址有一定关联,此处使用的是self.networkReachabiliy),
//然后当这个target的网络状态变化时,告之SCNetworkReachabilitySetCallback对象callout处理
//此处使用的是AFNetworkReachabilityCallback
//如果通知客户端设置成功,返回true
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
//表示在mainRunLoop中以kCFRunLoopCommonModes形式处理self.networkReachability
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
//在后台监测self.networkReachability的网络状态并使用SCNetworkReachabilityFlags函数返回的flag
//注意此处的flag就是网络状态
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
//将主线程的状态更改排入队列
AFPostReachabilityStatusChange(flags, callback);
}
});
}
//停止监听
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
//将networkReachability从runloop注销
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
在startMonitoring
方法当中,最后调用到AFPostReachabilityStatusChange
方法,该方法是用来将主线程的状态更改通知排入队列。下面看一下实现:
/**
将主线程的状态更改通知排入队列。
这样做是为了确保以与发送通知相同的顺序接收通知。
如果直接发送通知,则可能会在以后的更新后处理排队的通知(对于较早的状态条件),
从而导致侦听器处于错误的状态。
*/
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
//使用AFNetworkReachabilityStatusForFlags函数将flag转化为status,提供给blocks使用
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
//异步:在主线程中注册监听
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block(status);
}
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
这里做了两件事情:
- 将
flags
通过AFNetworkReachabilityStatusForFlags
进行解析,并返回当前网络状态 - 在主线程中根据当前网络状态注册网络状态改变时的监听
AFNetworkReachabilityStatusForFlags
方法用来解析flags
来返回当前的网络状态:
//返回网络状态
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
//该网络地址可达
BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
//该网络地址可达,但要先建立一个connection
BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
//虽然要先建立一个connection,但可以自动去connect
BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
//不需要用户交互就能connect(用户交互一般指的是提供网络的账号和密码)
BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
//如果isReachable==YES了,那么就需要判断是不是得先建立一个connection,如果需要,那就认为不可达,
//或者虽然需要先建立一个connection,但是不需要用户交互,那么认为也是可达的
BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
//状态
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
if (isNetworkReachable == NO) {
status = AFNetworkReachabilityStatusNotReachable;
}
#if TARGET_OS_IPHONE
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
status = AFNetworkReachabilityStatusReachableViaWWAN;
}
#endif
else {
status = AFNetworkReachabilityStatusReachableViaWiFi;
}
return status;
}
在进行开启和停止网络监听的时候,这里用到了SCNetworkReachability
这个类中的一些列内容,SCNetworkReachability
是可以获取网络状态同时也可以对网络进行检测的。在前面讲到的创建一个sessionManger
的时候(父类AFURLSessionManager
里的initWithSessionConfiguration:
方法),会初始化一个AFNetworkReachabilityManager
对象,用来进行网络检测,创建的方式是:
// AFNetworkReachabilityManager用来查看网络连接情况
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
看看单例初始化方法:
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager]; //调用了+ manager方法
});
return _sharedManager;
}
//在第一个单例方法中调用了此方法,通过默认的socket地址初始化,返回一个manager对象,
//sin_family表示协议族,AF_INET表示TCP/IP协议族的地址
+ (instancetype)manager {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address];
}
在第二种方法里面又调用了此方法,通过传入一个socket
地址来初始化:
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
在第三种方法里面,又调用了第四种初始化方法,因为该方法的后缀里面有NS_DESIGNATED_INITIALIZER
,所以最终都会调到它,这里就是做了初始化的工作,将起始的网络状态定为Unknown
:
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
此外,还有一种方法是可以根据特定的域来初始化。
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
数据解析
关于数据解析的类和接口,都存在AFURLResponseSerialization
这个文件中,先看一下和数据解析有关的类和协议:
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>
@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer
@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer
@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer
@interface AFImageResponseSerializer : AFHTTPResponseSerializer
@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer
父类是AFHTTPResponseSerializer
,遵守协议AFURLResponseSerialization
,其他的类都继承这个父类。
AFURLResponseSerialization协议
先看一下这个协议的接口:
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
注释写到:“AFURLResponseSerialization”协议由一个对象采用,该对象根据服务器响应中的详细信息将数据解码为更有用的对象表示形式。响应序列化程序还可以对传入的响应和数据执行验证。例如,JSON 响应序列化程序可能会检查可接受的状态代码(“2XX”范围)和内容类型(“application/json”),将有效的 JSON 响应解码为对象。
AFHTTPResponseSerializer
这个是所有其他解析类的父类,他遵守上面的AFURLResponseSerialization
协议。我们看一下协议在这个类中的实现:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}
这里调用了一个方法,进行了指定response
和数据的验证:
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;
//就是如果response不是nil,并且response的类型是NSHTTPURLResponse
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
//在上面最外层判断的内部是两个if判断,根据不同的条件判断数据是否有效以及在无效时应该抛出怎样的异常。
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
//如果有接受数据类型,如果不匹配response,而且响应类型不为空,数据长度不为0。接着进行判断,如果数据长度大于0,而且有响应URL,那么就生成mutableUserInfo信息
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;//这一定是抛出异常,没有验证通过的
}
//判断自己可接受的状态码,如果和response的状态码不匹配,则进入if块,生成错误和标识。
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;//这一定是抛出异常,没有验证通过的
}
}
//error和responseIsValid判断
//这里,如果error不为空,并且responseIsValid == NO,也就是说上面两个if判断至少走过了一个,这时候给error进行了赋值
if (error && !responseIsValid) {
*error = validationError;
}
//这个方法就是来判断返回数据与咱们使用的解析器是否匹配,需要解析的状态码是否匹配。
//两个属性值,一个acceptableContentTypes,一个acceptableStatusCodes,
//两者在初始化的时候有给默认值,如果给acceptableContentTypes定义了不匹配的类型,那么数据仍旧会解析错误
return responseIsValid;
}
AFJSONResponseSerializer
AFJSONResponseSerializer
是AFHTTPResponseSerializer
的一个子类,用于验证和解码JSON响应。
默认情况下,AFJSONResponseSerializer
接受以下MIME类型,其中包括官方标准application/json
以及其他常用类型:
application/json
text/json
text/javascript
看一下协议在这个类里的实现:
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
//有效性的验证
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
//数据无效后,返回nil
return nil;
}
}
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
//下面就是几个条件判断,满足的话直接序列化对应的JSON数据,不满足的话返回nil
//这里首先判断数据是否为空,利用isEqualToData:方法进行判断空格,如果不为空,并且数据长度大于0,那么就进行JSON数据的序列化
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length == 0 || isSpace) {
return nil;
}
NSError *serializationError = nil;
id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
if (!responseObject)
{
if (error) {
//如果error不为空,那么就利用函数AFErrorWithUnderlyingError生成NSError对象并赋值
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return nil;
}
//removesKeysWithNullValues:是否从响应JSON中删除具有NSNull值的键。
//默认为NO。如果需要移除这个键,那么就要调用下面的函数移除具有NSNull值的键
if (self.removesKeysWithNullValues) {
return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
return responseObject;
}
此外,我们再看一下移除具有NSNull值的键的函数:
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)];
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
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;
}
本节讲述了一个AFURLResponseSerialization
协议以及AFHTTPResponseSerializer
和AFJSONResponseSerializer
类中父类那个协议方法的实现。
UIKit+AFNetworking
在下一篇文章中再进行解释