您可能不知道运行循环在每个迭代中所做的一切.(在我研究这个答案之前,我不是!)事实上,CFRunLoop
是open-source CoreFoundation package的一部分,所以我们可以看看它到底意味着什么.运行循环大致如下所示:
while (true) {
Call kCFRunLoopBeforeTimers observer callbacks;
Call kCFRunLoopBeforeSources observer callbacks;
Perform blocks queued by CFRunLoopPerformBlock;
Call the callback of each version 0 CFRunLoopSource that has been signalled;
if (any version 0 source callbacks were called) {
Perform blocks newly queued by CFRunLoopPerformBlock;
}
if (I didn't drain the main queue on the last iteration
AND the main queue has any blocks waiting)
{
while (main queue has blocks) {
perform the next block on the main queue
}
} else {
Call kCFRunLoopBeforeWaiting observer callbacks;
Wait for a CFRunLoopSource to be signalled
OR for a timer to fire
OR for a block to be added to the main queue;
Call kCFRunLoopAfterWaiting observer callbacks;
if (the event was a timer) {
call CFRunLoopTimer callbacks for timers that should have fired by now
} else if (event was a block arriving on the main queue) {
while (main queue has blocks) {
perform the next block on the main queue
}
} else {
look up the version 1 CFRunLoopSource for the event
if (I found a version 1 source) {
call the source's callback
}
}
}
Perform blocks queued by CFRunLoopPerformBlock;
}
您可以看到,有多种方法可以连接到run循环中.你可以为任何你想要的"活动"创建一个CFRunLoopObserver
.您可以创建一个版本0 CFRunLoopSource
并立即发送信号.您可以创建一对连接的CFMessagePorts
,将其中一个封装在版本1 CFRunLoopSource
中,并向其发送消息.你可以创建一个CFRunLoopTimer
.您可以使用dispatch_get_main_queue
或CFRunLoopPerformBlock
对块进行排队.
您需要根据何时调度块以及何时需要调用块来决定使用这些API中的哪一个.
例如,touch 是在版本1源中处理的,但如果通过更新屏幕来处理touch ,则在提交核心动画事务之前,不会实际执行该更新,而核心动画事务是在kCFRunLoopBeforeWaiting
个观察者中发生的.
现在假设您希望在处理touch 时安排块,但希望在事务提交后执行.
您可以为kCFRunLoopBeforeWaiting
活动添加自己的CFRunLoopObserver
,但此观察者可能会在核心动画的观察者之前或之后运行,具体取决于您指定的顺序和核心动画指定的顺序.(Core Animation目前指定的订单为2000000,但没有记录,因此可能会发生变化.)
要确保块在核心动画的观察者之后运行,即使观察者运行before核心动画的观察者,也不要在观察者的回调中直接调用块.相反,此时使用dispatch_async
将块添加到主队列中.将块放在主队列上将迫使运行循环立即从其"等待"中唤醒.然后你的观察者会在任何时候阻塞你的主队列.