一、RunLoop和线程有什么关系?
每个线程,包括主线程(main thread),都有与之对应的RunLoop对象。
主线程的RunLoop是默认启动的,子线程的RunLoop默认是不开启的,需要手动开启子线程的RunLoop。
iOS程序里面,程序启动后会有这样的一个main()函数:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil,
NSStringFromClass([AppDelegate class]));
}
}
这是主循环,保证我们的程序一直运行下去。
UIApplicationMain()函数,这个方法会为main thread设置一个NSRunLoop对象,这就解释了:为什么我们的应用可以在无人操作的时候休息,需要让它干活的时候又能立马响应。
对其它线程来说,RunLoop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。
在任何一个Cocoa程序的线程中,都可以通过以下代码来获取到当前线程的RunLoop。
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
RunLoop在执行完毕后,就会进入休眠,只有在某个情况触发了,RunLoop才会继续被调用。
二、RunLoop的mode作用是什么?
Mode主要是用来指定事件在运行循环中得优先级,有:
1、NSDefaultRunLoopMode(KCFRunLoopDefaultMode) -> 默认
2、UITrackingRunLoopMode:ScrollView滑动时会切换到该Mode
3、UIInitializationRunLoopMode -> Run Loop启动时,会切换到该Mode
4、NSRunLoopCommonModes(KCFRunLoopCommonModes) -> Mode集合
苹果公开的Mode有两个:
NSDefaultRunLoopMode(KCFRunLoopDefaultMode)和NSRunLoopCommonModes(KCFRunLoopCommonModes)
在编程中如果我们把一个NSTimer对象以NSDefaultRunLoopMode(KCFRunLoopDefaultMode)添加到主运行循环中,ScrollView滚动过程中会因为Mode的切换,而导致NSTimer将不再被调度。当我们滚动的时候,也希望不调度,那就应该使用默认模式。但是,如果我们希望ScrollView在滚动时,计时器也要回调,那就应该使用NSRunLoopCommonModes(KCFRunLoopCommonModes)。
三、以+scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?
RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下来重启成新的mode。ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动。
如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候,ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。
所以:
Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。
四、猜想RunLoop内部是如何实现的?
本质:内部就是 do-while 循环,在这个循环内部不断地处理各种事件(任务),比如:Source、Timer、Observer。
每条线程都有唯一一个RunLoop对象与之对应,主线程的RunLoop默认已经启动 。
子线程的RunLoop需要手动启动。
每次RunLoop启动时,只能指定其中一个mode,这个mode被称作currentMode。
如果需要切换mode,只能退出loop,再重新指定一个mode进入,这样做主要是为了隔离不同mode中的Source、Timer、Observer,让其互不影响。
一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑是这样的:
function loop() {
initialize();
do {
var message = get_next_message();
process_message(message);
} while (message != quit);
}
伪代码while循环:
// http://weibo.com/luohanchenyilong/
// https://github.com/ChenYilong
int main(int argc, char * argv[]) {
//程序一直运行状态
while (AppIsRunning) {
//睡眠状态,等待唤醒事件
id whoWakesMe = SleepForWakingUp();
//得到唤醒事件
id event = GetEvent(whoWakesMe);
//开始处理事件
HandleEvent(event);
}
return 0;
}