AFNetworking2.6.3系列源码阅读
背景:关于NSURLConnection的发送一个网络请求时候。若在子线程调用同步接口,一条线程只能处理一个请求,因为请求一发出去线程就阻塞住等待回调,需要给每个请求新建一个线程,这是很浪费的,这种方式唯一的好处应该是易于控制请求并发的数量。但是我每次创建一个线程也是浪费线程资源,所以AFNetworking采用了一个常驻线程:
进入正题
1、AFNetworking,进入AFNetworking文件,找到AFURLConnectionOperation去seeing。
问题解答:AFNetworking是怎么解决常驻线程的了,其实很简单,用到了一个[NSMachPort port],我们知道应用在启动的时候有一个mainRunLoop的东西,这个就是一个while循环,其实底层源码实现就能看出。mainRunLoop为啥自己写的会占用内存比较好,而且CPU一直在执行了。其实自己也可以实现这种在某些特定情况下不占有CPU和内存的,使用睡眠,我在监听的port没有任务时候我就sleep,这不就简单的实现了吗。又说多了,哈哈,继续。我们在添加了一个[NSMachPort port]的东西后,而且[NSRunLoop currentRunLoop]在子线程中获取currentRunLoop后,就相当于当前的线程一直在监听这个currentRunLoop有没有任务,它怎么可能会执行完毕任务了。这就是AFNetworking简单实现常驻线程的原理。下面是代码
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
2、看到这儿都不用多想了,自己也可以。实现自己的NSRunLoop和常驻线程了,原来这么简单,其实不是。简单谈谈NSMachPort,其实端口通信只用于底层的通信,最早是服务器之间的通信,为啥了,我写一个命令或者其他过去不久可以了吗,这时候你想想,如果没有统一的格式限制,那么写出来的端口通信就千奇百怪,其实好处不止这点,就说这儿吧,继续下面的。
3、看看初始化方法
- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
NSParameterAssert(urlRequest);
......
}
4、NSParameterAssert这是什么鬼,其实了很简单,尤其是写工具的人一般都会用单,这是断言NSParameterAssert,可以理解为我肯定urlRequest有值,不然会阻止在这儿。
5、NSRecursiveLock 在初始化的时候创建一把递归锁,为啥了,因为人家考虑到在调用这个的时候可能递归使用,防止死锁。递归锁不清楚的可以看看八大锁。
6、下面看一行代码,这个只要是为了解决其他runLoopModes的模式的时候,网络请求这是事件类型不能回调,所以就采用一个NSRunLoopCommonModes类型,以为NSRunLoop在循环过程中只有一个类型的model :
self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
7、接下来看看重心位置,start函数,先贴代码:
方法1:
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
==========================================
方法2:
- (void)operationDidStart {
[self.lock lock];
if (![self isCancelled]) {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
[self.outputStream open];
[self.connection start];
}
[self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
});
}
==========================================
方法3:
- (void)cancelConnection {
NSDictionary *userInfo = nil;
if ([self.request URL]) {
userInfo = @{NSURLErrorFailingURLErrorKey : [self.request URL]};
}
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];
if (![self isFinished]) {
if (self.connection) {
[self.connection cancel];
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error];
} else {
// Accommodate race condition where `self.connection` has not yet been set before cancellation
self.error = error;
[self finish];
}
}
}
7、1 AFURLConnectionOperation 类继承 NSOperation ,NSOperation是一个抽象类,也是一个实现线程的抽象类。但是里面实现了start方法,可以看出里面实现了这些一部的方法,而且这个AFURLConnectionOperation是自定义这个任务结束的,因为它实现了start方法。
- (void)start {
......
}
==========================================
- (void)finish {
......
}
7、2 NSOperation是一个抽象类,然而start方法就算执行完毕,它的finish属性也不会变,所以要在执行完的地方自己调用finish方法,因此你可以控制这个operation的生命周期,可以cancle,或者其他操作。但是main的话,如果任务添加大queue中执行完毕后就会将任务移除,如果外部需要异步执行其他任务就不可以了。
7、3 在start函数中 performSelector 执行了在指定线程中执行 operationDidStart ,在 operationDidStart 中又调用了 lock ,递归锁的强大之处。,然后才真正的调用了发起网络请求者 NSURLConnection 的 initWithRequest ,后面的startImmediately表明非立刻请求。
7、4 scheduleInRunLoop是将self.connection该对象配置到一个runloop接收stream events,说明我当前的这种事件就像stream events没有结束状态,关于stream events有疑问,可以看下事件的分类,现在AFNet3.0采用的session事件方式。
7、5 看到这而不说 AFNetworking 当初设计者的那种把每个类都用的是极点。
7、6 后续其实内容的话见下一个版本。