田腾飞的博客

【iOS开发】沸沸腾带你深入浅出AFNetworking(流程实现篇)

怎么能快速提高自己的技术?一位大师说就是多看人家的开源代码,学习里面的设计思想,代码思想,多想多写多总结。

那么问题来了,我应该看哪个库?我的建议就是看跟自己技术相关的最热门的库,尽可能的去看。因为热门的库都是经过很多人的改良,很多大神的认可,里面的设计和思想都是值得肯定的。那么我们iOS开发最热门的库绝对当属AFNetworking。但是你是不是觉得你自己看不懂?也不知道怎么去读开源代码?小伙伴们别哭,今天我就带领你们一起去读热门开源代码,跟着我的脚步一步一步踏入知识的深渊。😏

开始分析

看AFNetwoking这个库之前,大家应该对NSURlSession有所了解,因为总所周知,这个库就是封装的NSURLSession,我们就来看看他是怎么封装的NSURLSession

先去github上下载AFNetworking,链接我就不贴了,一搜就是,下载下来,解压文件,打开工程,会看到下图:

  • 上面AFNetworking是库的代码,里面写的什么,怎么写的,假装我目前也是不知道的。
  • 下面是AFNetworking Example,呦呵!看名字就知道这里写的是例子。

要想学习了解一个库,首先我们得知道这个是库是怎么用的吧!在理不?好,那么多文件夹,那么多例子,我看到了iOS,哈哈,我想我们能看懂的的应该就是iOS这个example了吧。

这时候先运行一下这个例子,看看到底做了些什么?在选择运行的target中选择iOS Example

运行起来就是下面这个样子,下拉刷新会出现新的数据。OK,例子看起来很简单,就让我们看看是怎么实现的吧!

打开iOS Example代码文件,我们应该首先去查看AppDelegate.m文件,因为这里是程序的入口,程序可能会在里面做一些手脚。打开来看一下

我猜的没错,果然是做了手脚,我们仔细读一下代码。

  • 首先看着是创建了一个缓存,我的内心这时候想到了NSURLSession有三种config,一种是background,这种是可以允许在后台发送请求的,一种是ephemera,这种是资源都存在了运行内存中,还有一种是default,这一种是有缓存,必须手动创建一个缓存给他,那我们这个是不是跟这个有点关系呢?最后发现没有关系,看一下官方文档说:“当一个程序需要特别的存储要求的时候需要自己创建存储,也就是请求结果的缓存,比如一个页面的缓存,然后调用sharedURLCache方法”,上面config保存的主要是回话使用的证书,caches等,他们两个是不相同的。
  • 下面看到了AFN的使用,设置为YES,网络请求会改变状态栏的网络活动监视器。跟我们想要的发送网络请求没有关系的。
  • 继续往下看,貌似都是设置跟控制器的,目前来说只有GlobalTimelineViewController里面发送网络请求了,command点击进入.m文件。第一眼肯定会看到这里
1
2
3
4
5
6
7
8
9
10
11
12
- (void)reload:(__unused id)sender {
self.navigationItem.rightBarButtonItem.enabled = NO;
NSURLSessionTask *task = [Post globalTimelinePostsWithBlock:^(NSArray *posts, NSError *error) {
if (!error) {
self.posts = posts;
[self.tableView reloadData];
}
}];
[self.refreshControl setRefreshingWithStateOfTask:task];
}

我们看到的是一个reload方法,里面创建了一个task,这个task是后面Post执行的一个方法返回的对象,点击Post可以知道Post是一个类,他执行一个类方法,做一些事情,会返回他自身的一些请求对象,我猜Post执行的类方法一定会有网络请求,然后将请求的结果post数组返回回来。command点击进去。

哈哈,看到了网络请求就发生在这里,这里面就是把请求的数据进行转换,回调等。这里就是我们正式学习AFNetworking的正确入口了

1
return [[AFAppDotNetAPIClient sharedClient] GET:@"stream/0/posts/stream/global" parameters:nil progress:nil success:^(NSURLSessionDataTask * __unused task, id JSON)

command点击GET继续深入,看上面发现我们进入了AFHttpSessionManager.m文件了,终于正式进入了库中了,我早已经饥渴难耐了。外部调用了下面这个方法,但是这个方法里面又调用了dataTaskWithHTTPMethod:URLString: parameters:parameters uploadProgress:这个方法,返回一个dataTask,从方法参数中看的出来,可以监听上传进度和下载进度。下面调用了resume,让这个dataTask开始运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (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
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}

好了,上面这个方法,也没啥东西。我们继续点击dataTaskWithHTTPMethod:方法继续深入。我们又来到了下面这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (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];
////////////////
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
return dataTask;
}

这个方法里面我们看到了以下信息

  • 首先将传进来的method,URL, parameters等参数包装成了一个request
  • 将上面包装好的request传入到下面的那个方法dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:中,这个方法请求返回,有一个id类型的responseObject,这应该就是返回的一个数据对象。

好了,我们继续command点击下面那个方法进入下一层。看清楚我们进入了AFURLSessionManager.m文件中了,跟上面那几个方法不在一个文件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (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];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
  • 一眼看到了,原来是在这里创建的dataTask啊,使用的是self.session和传进来的request,可以看到AFURLSessionManager拥有一个NSURLSession的对象,但是外层那个creat_task_safely是个什么鬼?就我这暴脾气,不懂我就点击进去看。发现原来在iOS以前使用异步队列创建task会造成不正确的complectionHandler调用,iOS8以后的苹果把问题解决了。
  • 下面看着就是添加代理,我们点进去看看做了些什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (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
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
  • 这时候应该知道NSURLSession有两种方式处理请求得来的数据。一种是使用- (NSURLSessionDataTask *)dataTaskWithRequest: completionHandler:后面有completionHandler来处理数据。另一种就是添加delegate,这个delegate需要我们自己去创建,请求得到的数据等都是交给这个delegate去处理。

  • 所以我们目前请求得到的数据都会交给AFURLSessionManagerTaskDelegate这个类去处理。这里设置了他的数据请求完的completionHandler和上传进程处理的uploadProgressBlock还有下载进程处理的回到downloadProgressBlock

点击进去,可以看到这个delegate拥有一个AFURLSessionManager的对象,还有一些上传进程,下载进程等回调。

等等,AFN是怎么做到监听数据的上传或者下载进程的呢?
在这个delegate类中有一个- (void)setupProgressForTask:方法,其中使用了KVO来监听数据的变化。哈哈看到没我们又学到了KVO的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
............................
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.uploadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];

对应着的是下面KVO的observeValueForKeyPath方法的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
..........
else if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}

这里将执行我们传进来的上传进程或者下载进行的block,所以AFN做到上传下载进行就是使用了KVO。试着写一遍KVO你就会用KVO了,so easy!!!

好了,各位同志来往我们看下高潮部分,AFURLSessionManagerTaskDelegate肯定实现了NSURLSessionTaskDelegate的方法,也就是请求结束的方法。找到- (void)URLSession:task:didCompleteWithError:方法,当请求结束后一定会调用这个方法。看下重点部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&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];
});
});
});

当请求得到的数据,我们会使用上面我们设置好completionHandler来传出去,也就是请求结束后的数据会拿到我们外部处理。然后发出来一个通知,这个通知我全局查询了一下,貌似是一些工具类要用到,比如更新UI什么的。到此一个网络请求的整个流程就结束了。貌似也没啥东西啊???

等等。。。。。。。我继续滑动了下这个文件。看到了下面

1
2
3
4
5
6
7
8
9
10
11
12
+ (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));
}
}

看着好像是使用runtime来交换方法,我寻寻觅觅寻寻觅觅,突然发现紧挨着上面load方法中有下面几句代码

1
2
3
4
5
6
7
8
9
10
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)));
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
currentClass = [currentClass superclass];
}

断点调试,发现我们一开始就把NSURLSessionDataTaskresumesuspend方法替换了,并且将NSURLSessionDataTask所有父类的方法都替换了AFN自己的af_resumeaf_suspend方法。😱我擦咧,这是要做什么?看一下af_resume方法。

1
2
3
4
5
6
7
8
9
- (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];
}
}

不看不知道一看吓一跳啊,方法里面调用自己,这不是死循环么???我擦擦,仔细想一下明白了,因为前面已经把系统的resume方法和af_resume方法替换了,所以当调用系统的resume方法的时候实际上调用的是af_resume方法,里面又调用了af_resume方法,其实是调用的系统的resume方法,这样子就明白为啥不是死循环了吧。

看来这个替换体统的方法其实就是发出一个通知,当系统的resume方法被调用的时候发出通知,能让我们知道这个task什么时候开始执行,可以让我进一步对这个task进行控制。

整理一下整个请求流程

  • 当我们程序发送一个请求的时候,AFHTTPSessionManager会把我们的URL,method,parameter等包装成一个request对象
  • 然后把request传给AFURLSessionManagerAFURLSessionManager根据这个request对象创建一个dataTask
  • 然后给这个dataTask对象添加delegate,也就是AFURLSessionManagerTaskDelegate对象,并给delegate的上传和下载progress进行赋值
  • 使用KVO进数据变化进行监听,来改变uploadProgress和downloadProgress
  • delegate中实现NSURLSessionTaskDelegate方法,把得到的数据进行回传给completeHandler回调,并发出通知
  • 使用runtime替换系统的resume和suspend方法,然后当task开始的时候发出通知,已达到对task控制的地步。

到这里是不是对AFNetworking一个请求的流程搞明白了?是不是很简单?这么库比较庞大,这篇文章肯定深入浅出不完,请关注我的后续文章继续解读AFNetworking吧。

小伙伴们如果感觉文章可以,可以关注博主博客

小伙伴们也可以关注博主微博,探索博主内心世界😁

如要转载请注明出处。

热评文章