每次, RunLoop 会操作输入的 events(各种 input source 产生的) 并产生 notifications(给 observers). RunLoop 的处理 events 并且发出 notifications 的具体的顺序如下:
1> 发 notification,通知 Observer,当 RunLoop 开始进入循环的时候
2> 发 notification,通知 Observer, timers 即将被触发(处理 timers 的event)
3> 发 notification,通知 Observer,有其他的非 Port-Based Input Source 即将被触发(non-port-based input source 的 event)
4> 启动非 Port-Based Input Soruce 的事件源的处理函数
5> 如果基于Port的Input Source事件源即将触发时,立即处理该事件,并跳转到9
6> 发 notification,通知 Observer,当前 thread 即将进入睡眠状态
7> 使线程进入睡眠状态直到有以下事件发生 :
1.Port-Based Input Source event
2.Timer fires
3.RunLoop 设置的时间超时
4. RunLoop 被代码显示唤醒
8> 发 notification,通知 Observer, thread将要被唤醒
9> 处理被触发的事件
1.如果是用户自定义的Timer触发的,处理Timer事件的函数后,重启Run Loop.直接进入步骤2
2.如果是 input source 事件源有事触发 event,直接传递这个消息
3.如果runloop 是显示被唤醒并且没有超时,重启 RunLoop. 直接进入步骤2
10> 发 notification,通知 Observer,Run Loop已经退出
由于 timer 和 input source导致 runloop 给 observer 发送 notification 是在这些事件之前的, 因此可能 notification 发出以后到事件真正的发生中间会有一小段间隔时间, 可以通过 awake-from-sleep 的 notification 来修复确切的时间.
因为 timers 和其他与时间片相关的事件会在runloop 运行时候传递, 有时候会使得循环传递这些事件失灵.典型例子就是, 你在进入 runloop 时候,监听了鼠标的移动路线,然后不断在 app 中请求一些事件.因为你的代码中已经抓住这些事件,并直接处理了,而不是让 app 正常的去 dispatch事件,活动着的 timers 讲将不能被fire,直到你的鼠标追踪事件停止,并把控制权交给 app.
RunLoop 可以使用 runLoop 对象显示的唤醒. 其他的事件也能够唤醒 runloop.例如,给 runloop 添加一个 non-port-based input source,就可以唤醒它,而不必等到其他的事件发生.