iOS开发——AFNetworking源码(二)
上篇文章主要介绍了AFNetworking的简单介绍和使用,熟悉了AFNetworking的基本结构,那么本文我们主要介绍一下网络通信模块(AFURLSessionManger,AFHTTPSessionManager),首先我们来看看这两个类之间的关系,以及他们内部都包含哪些模块,用来做什么的,我们先来看一下下面这张图:
由上图可见,首先AFURLSessionManger是AFHTTPSessionManager的父类,其中AFURLSessionManger内部还包含AFURLSessionManagerTaskDelegate和_AFURLSessionTaskSwizzling;AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;
AFURLSessionManger
AFURLSessionManger功能作用
AFURLSessionManager负责生成对应的NSURLSession的实例,管理AFNetworkReachabilityManager和AFSecurityPolicy,以此一来查看网络的连接情况,二来保证请求的安全,同时初始化生成一个AFJSONResponseSerializer的实例来序列化HTTP的响应结果;
AFURLSessionManger的属性和接口方法
核心的方法包括:初始化方法;针对不同任务的request方法;
初始化方法的实现在上文已经详细讲过,最终生成一个AFURLSessionManager的实例对象;上文我们介绍了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这个方法上文具体讲述过;
接下来我们再看看downloadTaskWithRequest
//使用指定的请求创建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设置代理,最终将其返回;
uploadProgressForTask和downloadProgressForTask 这两个方法是用来获取上传或者下载的进度;另外还有一些自定义的block的set方法。关于这一点,里边使用到的一些自定义的block回调,作者在.m文件中声明一些block属性,并且复写了其set方法,然后又在.h文件中声明这些set方法:这样做的目的看来是为了使用方便,我们在调用set方法设置这些block,能够很清晰的看到block的各个参数与返回值;
代理方法实现
上文最后提到AFURLSessionManager遵守的代理都有NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying;对应实现了这些代理中的一些方法,源代码如下:
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
error = objc_getAssociatedObject(task, AuthenticationChallengeErrorKey) ?: error;
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
//We no longer need the reference, so nil it out to gain back some memory.
self.mutableData = nil;
}
#if AF_CAN_USE_AT_AVAILABLE && AF_CAN_INCLUDE_SESSION_TASK_METRICS
if (@available(iOS 10, macOS 10.12, watchOS 3, tvOS 10, *)) {
if (self.sessionTaskMetrics) {
userInfo[AFNetworkingTaskDidCompleteSessionTaskMetrics] = self.sessionTaskMetrics;
}
}
#endif
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
遵守的这些代理,最终AFURLSessionManager对这些代理做了一些公共的处理,最终转发到自定义的代理AFURLSessionManagerTaskDelegate的3个代理方法中,用来负责把每个task对应的数据回调回去;
在AFURLSessionManager里边重要的代理包括NSURLSessionTaskDelegate,NSURLSessionDataDelegate以及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中的这三个代理方法都做了什么 吧:
- (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的代理中实现
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函数。
+ (void)load {
/**
WARNING: Trouble Ahead
https://github.com/AFNetworking/AFNetworking/pull/2702
*/
//判断当前的iOS版本是否含有NSURLSessionTask类
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方法的实现(返回给指定类的实例方法)method_getImplementation:返回方法的实现 class_getInstanceMethod
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和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));
}
}
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:请求序列化器)
//1.⚠️使用请求序列化类中的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;
}
//NSURLSessionManger创建一个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;
}
该方法做了两件事情:一是对请求参数进行序列化;二是调用dataTaskWithRequest方法生成后一个datatask任务;最终返回一个datatask;
关于网络通信模块(AFURLSessionManger,AFHTTPSessionManager)的总结
网络通讯模块所做的事情大概就这么多,在该模块中主要的任务就是发起网络请求。分成AFURLSessionManger和 AFHTTPSessionManager两部分来做处理。 AFURLSessionManger是AFHTTPSessionManager的父类,其中AFURLSessionManger内部还包含AFURLSessionManagerTaskDelegate和_AFURLSessionTaskSwizzling;AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;其内部管理自己的两种序列化工具,用来对请求和响应的数据做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;