RunLoop的事件队列
2016-06-14 本文已影响182人
面糊
RunLoop的事件队列
-
每次运行RunLoop, 线程中的RunLoop会自动处理线程中的任务, 并且通知观察者, 汇报当前的状态, 顺序如下
- 通知观察者RunLoop已经启动
- 通知观察者任何即将要开启的定时器
- 通知观察者任何即将要启动的非基于端口的事件源
- 启动任何准备好的非基于端口的事件源
- 如果基于端口的事件源准备好并处于等待状态, 就立即启动, 并且进入步骤9
- 通知观察者线程即将进入休眠
- 将线程置于休眠状态, 直至以下事件的发生
- 某一事件到达基于端口的源事件
- 定时器启动
- RunLoop设置的事件已经超时
- RunLoop被显式唤醒
- 通知观察者线程即将被唤醒
- 处理事件
- 如果用户定义的定时器启动, 处理定时器事件并且重启RunLoop, 然后进入步骤2
- 如果输入源启动, 传递响应的信息
- 如果RunLoop被现实唤醒, 并且事件还没超时, 重启RunLoop, 进入步骤2
- 通知观察者RunLoop结束
-
代码解释
// 用DefaultMode启动 void CFRunLoopRun(void) { CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); } // 用指定的Mode启动,允许设置RunLoop超时时间 int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) { return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); } // RunLoop的实现 int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) { // 首先根据modeName找到对应mode CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false); // 如果mode里没有source/timer/observer, 直接返回。 if (__CFRunLoopModeIsEmpty(currentMode)) return; // 1. 通知 Observers: RunLoop 即将进入 loop。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry); // 内部函数,进入loop __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) { Boolean sourceHandledThisLoop = NO; int retVal = 0; do { // 2. 通知 Observers: RunLoop 即将触发 Timer 回调。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers); // 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources); // 执行被加入的block __CFRunLoopDoBlocks(runloop, currentMode); // 4. RunLoop 触发 Source0 (非port) 回调。 sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle); // 执行被加入的block __CFRunLoopDoBlocks(runloop, currentMode); // 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。 if (__Source0DidDispatchPortLastTime) { Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg) if (hasMsg) goto handle_msg; } // 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。 if (!sourceHandledThisLoop) { __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting); } // 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。 // ? 一个基于 port 的Source 的事件。 // ? 一个 Timer 到时间了 // ? RunLoop 自身的超时时间到了 // ? 被其他什么调用者手动唤醒 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) { mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg } // 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting); // 收到消息,处理消息。 handle_msg: // 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。 if (msg_is_timer) { __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time()) } // 9.2 如果有dispatch到main_queue的block,执行block。 else if (msg_is_dispatch) { __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); } // 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件 else { CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort); sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg); if (sourceHandledThisLoop) { mach_msg(reply, MACH_SEND_MSG, reply); } } // 执行加入到Loop的block __CFRunLoopDoBlocks(runloop, currentMode); if (sourceHandledThisLoop && stopAfterHandle) { // 进入loop时参数说处理完事件就返回。 retVal = kCFRunLoopRunHandledSource; } else if (timeout) { // 超出传入参数标记的超时时间了 retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(runloop)) { // 被外部调用者强制停止了 retVal = kCFRunLoopRunStopped; } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) { // source/timer/observer一个都没有了 retVal = kCFRunLoopRunFinished; } // 如果没超时,mode里没空,loop也没被停止,那继续loop。 } while (retVal == 0); } // 10. 通知 Observers: RunLoop 即将退出。 __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); }
-
图解
322CEF6D-7766-4EE3-9169-40D5F564CDD1.png -
RunLoop的一般应用
- NSTimer和GCD定时器
- PerformSelector: afterDelay
- 当调用这个方法的时候, 实际内部会创建一个Timer并且添加到当前的RunLoop中, 如果当前线程没有RunLoop, 这个方法也就会失效
- 在子线程中开启一个RunLoop, 做为常驻线程
- 自动释放池
- 手势识别等等