本博客借鉴自大佬博客:大佬博客
了解了AFNetworking的基本框架和以GET网络请求为例的请求原理流程之后,我们来以板块的形式了解一下AFURLSessionManger和AFHTTPSessionManager这两个通信模块。
首先看下图:
我们知道AFURLSessionManager是AFHTTPSessionManager的父类,其中AFURLSessionManager内部还包含AFURLSessionManagerTaskDelegate和_AFURLSessionTaskSwizzling;
AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的,其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化,同时依赖于负累提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能
下面分别来介绍这两者:
一、AFURLSessionManger
1. AFURLSessionManger的功能:
AFURLSessionManger 负责生成对应的NSURLSession的实例,管理AFNetworkReachabilityManager和AFSecurityPolicy,以此一来查看网络的连接情况,二来保证请求的安全,同时初始化生成一个AFJSONResponseSerializer的实例来序列化HTTP的响应结果;
AFURLSessionManger的属性和接口方法:
其中的核心方法包括:初始化方法、针对不同任务的request方法;
初始化方法的实现在上文已经详细讲过,最终生成一个AFURLSessionManager的实例对象;上文在以GET请求为例的时候发送请求过程的时候介绍了dataTaskWithRequest,这里主要来看一下uploadTaskWithRequest、downloadTaskWithRequest和downloadTaskWithResumeData这三个方法:
//使用本地文件的指定请求创建一个NSURLSessionUploadTask
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
//根据request以及要上传的本地文件的URL创建一个uploadTask
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
//为uploadTask设置代理
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
{
NSURLSessionUploadTask *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
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
uploadTaskWithRequest根据不同的数据创建一个NSURLSessionUploadTask任务,最终都会走到addDelegateForUploadTask为对应的uploadTask设置代理;addDelegateForUploadTask这个方法之前提到过;
//使用指定的请求创建NSURLSessionDownloadTask
- (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
{
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
downloadTaskWithRequest:使用指定的请求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
{
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData];
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
downloadTaskWithResumeData:使用已经下载的部分数据 resumeData 创建一个下载任务,继续进行下载。并且为所创建的task设置代理,最终将其返回;
还有下方这两个方法:
- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task {
return [[self delegateForTask:task] uploadProgress];
}
- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task {
return [[self delegateForTask:task] downloadProgress];
}
这两个方法是用来获取上传或者下载的进度;
另外作者还写了一些自定义的block的set方法,作者在.m文件中声明一些block属性,并且复写了其set方法,然后又在.h文件中声明这些set方法,这样做的目的看来是为了使用方便,我们在调用set方法设置这些block,能够很清晰的看到block中的各个参数和返回值;
代理实现:
AFURLSessionManager遵守的代理都有NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying对应实现了这些代理中的一些方法
遵守的这些代理,最终AFURLSessionManager对这些代理做了一些公共的处理,最终转发到自定义的代理AFURLSessioniManagerTaskDelegate的3个代理方法中,用来负责把每个task对应的数据回调回去;
在AFURLSessionManager里边重要的代理包括NSURLSessionTaskDelegate,NSURLSessionDataDelegate以及NSURLSessionDownloadDelegate,这里重点是看源码的实现,以下是AFURLSessionManager实现的NSURLSessionDownloadDelegate代理:
AFURLSessionManager中实现了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];
} else {
//成功,发送会话下载任务成功移动文件通知
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
}
return;
}
}
//进行代理转发
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
//周期性地通知下载进度调用
//bytesWritten 自上次调用方法后,接收到的数据字节数
//totalBytesWritten 目前已经接收到的数据字节数
//totalBytesExpectedToWrite 期望收到的文件总字节数 是由content-Length header提供,如果没有提供默认是NSURLSessionTransferSizeUnknown
- (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);
}
}
//当下载被取消或失败后重新恢复下载后调用 告诉代理下载任务重新开始下载了 didResumeAtOffset:在偏移中恢复,即从哪里开始恢复下载 expectedTotalBytes:预期下载的总字节数
- (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];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidMoveFileSuccessfullyNotification object:downloadTask userInfo:nil];
}
}
}
}
在下载完成时的调用代理方法中,AFURLSessionManager和AFURLSessionManagerTaskDelegate中都进行了文件路径的移动,而NSURlSession代理的下载路径是所有request公用的下载路径,设置之后所有的request都会下载到之前的那个路径。而AFURLSessionManagerTaskDelegate中对应到每一个task中,每一个task可以设置自己的下载路径;
总结: 这些代理方法在AFURLSessionManager中实现的时候都是对session做一个公共的处理,每一个不同的task进行特定的处理时,需要将代理转发到AFURLSessionManagerTaskDelegate中,在AFURLSessionManagerTaskDelegate的代理中实现;
2.AFURLSessionManagerTaskDelegate的作用
主要用来管理进度;并且在task结束的时候回调使用;在上述提到AFURLSessionManagerTaskDelegate中关于NSURLSessionDownloadDelegate的代理方法实现,对应到每一个task中,每一个task可以设置自己的下载路径;相应的也实现了NSURLSessionDataDelegate,NSURLSessionTaskDelegate;这些代理都是用来对当前特定的task做处理;
监听的处理方法,observeValueForKeyPath,这个方法是用来当datatask状态发生改变时的监控处理逻辑,调用block回调,用户拿到进度;
3. _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函数。
+ (void)load {
/**
WARNING: Trouble Ahead
https://github.com/AFNetworking/AFNetworking/pull/2702
*/
//判断当前的iOS版本是否含有NSURLSessionTask类
if (NSClassFromString(@"NSURLSessionTask")) {
/**
iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky.
Many Unit Tests have been built to validate as much of this behavior has possible.
Here is what we know:
- NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back.
- Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there.
- On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`.
- On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`.
- On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled.
- On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled.
- Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there.
Some Assumptions:
- No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
- No background task classes override `resume` or `suspend`
The current solution:
1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
2) Grab a pointer to the original implementation of `af_resume`
3) Check to see if the current class has an implementation of resume. If so, continue to step 4.
4) Grab the super class of the current class.
5) Grab a pointer for the current class to the current implementation of `resume`.
6) Grab a pointer for the super class to the current implementation of `resume`.
7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods
8) Set the current class to the super class, and repeat steps 3-8
*/
//创建一个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方法的实现 (返回给指定类的实例方法)method_getImplementation:返回方法的实现。 class_getInstanceMethod
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
//循环去curentcalss中查看是否有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和suspenf方法
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
}
//调剂theClass的resume和suspenf方法
+ (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));
}
}
- (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的功能:
AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;
属性和接口:
来看看AFHTTPSessionManager的一些方法的实现,初始化方法最终都会调用到AFURLSessionManager中的初始化方法完成sessionManager的初始化;
以GET为例,发送一个网络请求的时候:
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
//调用dataTaskWithHTTPMethod方法生成一个dataTask任务
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
headers:headers
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:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
//序列化错误
NSError *serializationError = nil;
//设置request相关属性&参数,(requestSerializer:请求序列化器)
//调用请求序列化类中的requestWithMethod方法进行序列化处理
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//设置请求头
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
//序列化失败的回调处理
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请求的核心功能;