AFNetworking 3.0源码阅读笔记
框架重要类介绍
NSURLSession
AFURLSessionManager AFHTTPSessionManager
序列化 AFURLRequestSerialization AFURLResponseSerialization
附加功能 AFSecurityPolicy AFNetworkReachabilityManager
其中 AFURLSessionManager 是 AFHTTPSessionManager 的父类
AFURLSessionManager 负责生成 NSURLSession 的实例,管理 AFSecurityPolicy 和 AFNetworkReachabilityManager,来保证请求的安全和查看网络连接情况,它有一个 AFJSONResponseSerializer 的实例来序列化 HTTP 响应
AFHTTPSessionManager 有着自己的 AFHTTPRequestSerializer 和 AFJSONResponseSerializer 来管理请求和响应的序列化,同时依赖父类提供的接口保证安全、监控网络状态,实现发出 HTTP 请求这一核心功能
AFURLSessionManager 请求和管理核心
负责创建和管理 NSURLSession
管理 NSURLSessionTask
实现 NSURLSessionDelegate 等协议中的代理方法
使用 AFURLSessionManagerTaskDelegate 管理进度
引入 AFSecurityPolicy 保证请求的安全
引入 AFNetworkReachabilityManager 监控网络状态
使用 AFURLSessionTaskSwizzling 调剂方法
创建和管理 NSURLSession
由 AFURLSessionManager 的初始化方法:
- (instancetype)initWithSessionConfiguration:
(nullable NSURLSessionConfiguration *)configuration;
进行展开:
设计模式-- 构造函数,参数控制在三个以内。
正常业务逻辑会出现参数过多的情况。
解决方案
- (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)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
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];
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
[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;
}
该方法主要完成如下工作:
初始化会话配置(NSURLSessionConfiguration),默认为 defaultSessionConfiguration
设置相应的 OperationQueue,决定请求过程中的一系列事件在哪个 OperationQueue 回调,这里是设置了最大并发量为 1 的队列,也就相当于串行队列了。
初始化会话(session),并设置会话的代理及代理队列,delegate 用来处理请求中的各种事件,可以设置为 nil 使用系统提供的 delegate,
另外,NSURLSession 对象是强引用了 delegate
如果程序最终没有调用 invalidateAndCancel 方法来 invalidate 该 session 的话,则会造成内存泄漏
初始化管理响应序列化(AFJSONResponseSerializer),安全认证(AFSecurityPolicy)以及监控网络状态(AFNetworkReachabilityManager)的实例
初始化保存 data task 的字典(mutableTaskDelegatesKeyedByTaskIdentifier)
创建和管理 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;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
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;
}
设计模式: NSError真滴好用。特别是对于串行的错误判断代码。
// 创建request对象
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// mark:YD ARC下,autoreleasrpool依然好用。
// 猜测可能是由于如果用strong,则会导致NSError对象。在整个方法周期内。不会被释放掉。为了节省内存。使用__autoreleasing,在其作用域以外释放
next: 后来证明这个猜测只有部分猜对了.
事实上,**类型作为参数传入方法里.即便不加autoreleasrpool修饰符,系统也会帮助添加;
真正的原因是跟函数作用域相关.传入的参数会有指针拷贝操作,系统为了其生成的指针在作用域负责释放,故添加修饰.
PS : **传入地址.拷贝的也就是指向指针的指针了.用完是要释放掉的,但地址并不能一定在其作用域内释放.详情见部分系统方法传入&error这种类型.地址在方法外也会被引用.
故只能用__autoreleasing修饰,在其地址真正无作用时再释放掉.
参考资料: http://daiyi.pro/2017/01/07/%E4%BA%8C%E7%BA%A7%E6%8C%87%E9%92%88%E4%B8%8EARC%E4%B8%8D%E4%B8%BA%E4%BA%BA%E7%9F%A5%E7%9A%84%E7%89%B9%E6%80%A7/
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// mark:YD 利用kvo,将manager上有关于request的值,赋值给当前的request
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
实现 NSURLSessionDelegate 等协议中的代理方法
- NSURLSessionDelegate
NSURLSessionTaskDelegate,遵守 NSURLSessionDelegate 协议
NSURLSessionDataDelegate,遵守 NSURLSessionTaskDelegate 协议,是网络请求通常遵循的协议,常用的方法:
接受到服务响应时调用的方法:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
/**
* 必须在该方法中对服务器的响应进行授权,才能继续接收服务器返回的数据,调用如下函数
* completionHandler(NSURLSessionResponseAllow)
*/
接收到服务器返回的数据时调用的方法
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
/**
* data:服务返回的数据,通常为 JSON 格式数据
*/
请求完成时调用的方法(成功或失败)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
/**
* 若出现错误,error 中存放错误信息
*/
- NSURLSessionDownloadDelegate(通常用于下载大量数据),遵守 NSURLSessionTaskDelegate 协议,常用的方法:
写入数据到临时文件时调用的方法(服务器返回一点就写入一点)
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite: (int64_t)totalBytesExpectedToWrite
/**
* totalBytesWritten,已写入数据的总长度
* totalBytesExpectedToWrite:总共要写入数据的总长度
* 可以在该方法中计算下载进度
*/
遇到错误的时候调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
/**
*error:若遇到错误,则保存错误信息
*/
用于断点下载的方法
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
/**
* fileOffset:继续下载时,文件的开始位置
* expectedTotalBytes:剩余的数据总数
*/
下载完成时调用的方法
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
/**
* location:下载的文件保存的临时位置
* 需要将下载的文件保存在可以长期保存的位置
*/
使用 AFURLSessionManagerTaskDelegate 管理进度
是在太TMD复杂,留着以后再看
使用 _AFURLSessionTaskSwizzling 调剂方法
AFURLSessionTaskSwizzling 的唯一功能就是修改 NSURLSessionTask 的 resume 和 suspend 方法,使用下面的方法替换原有的实现:
- (void)af_resume {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_resume];
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];
}
}
- 这样做的目的是为了在方法 resume 或者 suspend 被调用时发出通知。具体方法调剂的过程是在 + load 方法中进行的
+ (void)load {
if (NSClassFromString(@"NSURLSessionTask")) {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
// 首先构建一个 NSURLSession 对象 session,再通过 session 构建出一个 _NSCFLocalDataTask 变量
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
// 获取到 af_resume 实现的指针
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
// 检查当前 class 是否实现了 resume。如果实现了,继续第 4 步
while (class_getInstanceMethod(currentClass, @selector(resume))) {
// 获取到当前 class 的父类(superClass)
Class superClass = [currentClass superclass];
// 获取到当前 class 对于 resume 实现的指针
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
// 获取到父类对于 resume 实现的指针
IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
// 如果当前 class 对于 resume 的实现和父类不一样(类似 iOS 7 上的情况),并且当前 class 的 resume 实现和 af_resume 不一样,才进行 method swizzling
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
// 设置当前操作的 class 为其父类 class,重复步骤 3~8
currentClass = [currentClass superclass];
}
引入 AFSecurityPolicy 保证请求的安全
AFSecurityPolicy 是 AFNetworking 用来保证 HTTP 请求安全的类,它被 AFURLSessionManager 持有,如果你在 AFURLSessionManager 的实现文件中搜索 self.securityPolicy,你只会得到三条结果:
初始化 self.securityPolicy = [AFSecurityPolicy defaultPolicy]
收到连接层的验证请求时
任务接收到验证请求时
在 API 调用上,后两者都调用了
- [AFSecurityPolicy evaluateServerTrust:forDomain:]
方法来判断当前服务器是否被信任。
引入 AFNetworkReachabilityManager 监控网络状态
没看,下次再说。。