AFN源码阅读笔记(一)看看一个请求怎么生成

AFNetworking 3.0源码阅读笔记

AFN源码阅读笔记(一)看看一个请求怎么生成_第1张图片

框架重要类介绍

  • NSURLSession

  • AFURLSessionManager AFHTTPSessionManager

  • 序列化 AFURLRequestSerialization AFURLResponseSerialization

  • 附加功能 AFSecurityPolicy AFNetworkReachabilityManager

AFN源码阅读笔记(一)看看一个请求怎么生成_第2张图片
AFN架构体示意图

其中 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 等协议中的代理方法

AFN源码阅读笔记(一)看看一个请求怎么生成_第3张图片
首先,NSURLSession 的代理对象结构如下:
  • 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 监控网络状态

没看,下次再说。。

你可能感兴趣的:(AFN源码阅读笔记(一)看看一个请求怎么生成)