RunLoop 的定义
什么是RunLoop?
//伪代码
while (true) {
Source* source = SleepAndWaitWakeUp();
Event* event = GetEventBySource(source);
HandleEvent(event);
}
为什么需要RunLoop?
试想一个,我们启动一个App,如何保证它一直运行着呢?我们来看下main函数的几行代码:
int main(int argc, char * argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([WYAppDelegate class]));
}
}
这个就是iOS的启动函数,若UIApplicationMain没有事件循环,那么就直接return了,App就无法运行了,所以UIApplicationMain里面肯定是在主线程开启了RunLoop。
那为不直接使用While等这种死循环呢?为何会创造一个RunLoop这个玩意呢?
- RunLoop虽然我们可以理解成是一个死循环,但它不单单只有循环,当然还有很多其他功能。
- 降低CPU消耗,若While的话,CPU是一直在使用的,RunLoop在睡眠时是不消耗CPU的。
RunLoop的作用
- 保持程序持续运行
- 处理app中个各种事件(触摸,定时器,performSelector)
- 节省cpu资源,提供程序的性能,有事做,做事,没事做休息。
在Cocoa开发中,哪些功能是用到了RunLoop呢?
- NSTimer
- UIEvent
- Autorelease
- CADisplayLink
- CATransition
- CAAnimation
- GCD - dispatch_get_main_queue
- NSObject (NSThreadPerformAdditions)
- NSObject (NSDelayedPerforming)
- NSURLConnection
…
其实在主线程的任何一个断点,都可以看到调用堆栈信息里面都有CFRunLoop这家伙的影子,一般都是由以下6个回调上来的:
source0
CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
- 发 Source0 (非基于port的) 回调,处理如UIEvent,CFSocket这类事件。需要手动触发。
- 触摸事件其实是Source1接收系统事件后在回调__IOHIDEventSystemClientQueueCallback()内触发的Source0
source1
CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
- ///如果如果Runloop是被 Source1 (基于port的) 的事件唤醒了,
- 处理这个事件 ///处理系统内核的mach_msg事件
queue
CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
///被dispatch唤醒,执行放入main_queue的block
time
CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
被timer唤醒
observer
CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
///通知observer当前runloop的状态
block
CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
///非延迟的block事件调用,CFRunLoopPerformBlock,立即执行一个block