2018-05-24
iOS程序的入口是main函数,在main函数里int a = UIApplicationMain之前的代码可以执行,而在其后面的却不能执行。是因为UIApplicationMain里面开启了一个死循环(Runloop 循环)。
意义
- 保住当前线程的生命。
- 监听事件:触摸、时钟、网络、UI等等!
- (void)viewDidLoad {
[super viewDidLoad];
NSTimer * timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(otherMethod) userInfo:nil repeats:YES];
}
-(void)otherMethod{
NSLog(@"OtherMethod --- %@",[NSThread currentThread]);
}
以上的otherMethod方法并不会执行,timer被释放了,通过在viewDidLoad后面添加下面的代码可以证明。
__weak typeof(timer) aaaaa = timer;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (aaaaa == nil) {
NSLog(@"释放了");
}
});
那么应该如何解决呢?我们将timer添加到Runloop当中添加以下代码
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
运行后otherMethod开始执行了,timer也不释放了。
上面说Runloop会监听UI事件,如果在app里添加一给UIScrollView并在运行后滚动它,会发现otherMethod方法停止了调用,停止滚动后就恢复了。这是为什么呢?
Runloop监听所有事件是分模式的,我的理解就是Runloop将处理的事件分类,当Runloop处在一个模式下就会处理对应的类别的事件。
- 默认模式NSDefaultRunLoopMode
- UI模式UITrackingRunLoopMode
前面将timer添加到Runloop的时候传的参数是NSDefaultRunLoopMode就相当于将timer产生的事件标记为Runloop在默认模式下才会监听的事件,而在滑动UIScrollView时Runloop的UI模式下的Source会产生事件,又因为UI模式的优先级高,Runloop会切换到UI模式(即:默认模式下的timer和UI模式下的Source同时产生事件时,会监听后者),这时otherMethod方法就不会执行了。
将NSDefaultRunLoopMode改为UITrackingRunLoopMode,触摸并滑动UIScrollView后可以发现otherMethod方法开始执行了,松开手后又停止执行了,是因为UI模式只会被触摸事件所触发
为了让拖拽与否都能处理Timer事件可以这样
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
将Timer分别添加到两个模式中去,但是苹果有更好的办法,向下面的那样。
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
NSRunLoopCommonModes为占位模式,相当于NSDefaultRunLoopMode和UITrackingRunLoopMode的两个模式的组合。