gpt4 book ai didi

ios - iOS runloop 中的操作顺序

转载 作者:可可西里 更新时间:2023-11-01 03:24:30 25 4
gpt4 key购买 nike

iOS 上的操作顺序是什么?

我正在特别考虑时间安排

  • setNeedsLayoutlayoutSubviews
  • setNeedsDisplaydrawRect
  • 触摸识别
  • [NSTimer scheduledTimerWithTimeInterval:0.000001 tar(...)]
  • dispatch_async(dispatch_get_main_queue(), ^{/* code */>

作为我希望收到的答案的示例,它可以采用以下格式:

dispatch_async on main Happens before the next runcycle

drawRect Happens at the end of the runcycle

最佳答案

(部分内容复制自 my answer to a similar question 。)

原来run loop很复杂,像“Does drawRect: happen at the end of the runcycle?”这样简单的问题没有简单的答案。

CFRunLoopopen-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 signaled;
// Touch events are a version 0 source in iOS 8.0.
// CFSocket is a version 0 source.
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)
{
remove all blocks from the main queue
execute all the blocks just removed from the main queue
} else {
Call kCFRunLoopBeforeWaiting observer callbacks;
// Core Animation uses a BeforeWaiting observer to perform layout and drawing.
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) {
remove all blocks from the main queue
execute all the blocks just removed from 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
}
// Interface orientation changes are a version 1 source in iOS 8.0.
}
}
Perform blocks queued by CFRunLoopPerformBlock;
}

Core Animation 注册了一个 kCFRunLoopBeforeWaiting 观察器,其顺序为 2000000(尽管没有记录;您可以通过打印 [NSRunLoop mainRunLoop].description 来弄清楚)。这个观察者提交当前的 CATransaction,它(如果需要)执行布局(updateConstraintslayoutSubviews)然后绘制(drawRect:)。

请注意,运行循环可以在执行 BeforeWaiting 观察器之前两次评估 while(true) 中的 true。如果它调度计时器或版本 1 源,并且将 block 放在主队列上,则运行循环将在调用 BeforeWaiting 观察器之前运行两次(并且它会两次调度版本 0 源)。

系统混合使用版本 0 源和版本 1 源。在我的测试中,触摸事件是使用版本 0 源传递的。 (您可以通过在触摸处理程序中放置一个断点来判断;堆栈跟踪包含 __CFRunLoopDoSources0。)进入/离开前台等事件是通过 CFRunLoopPerformBlock 调度的,所以我不知道什么样的来源真正提供了它们。界面方向更改通过版本 1 源提供。 CFSocket is documented to be a version 0 source. (很可能 NSURLSessionNSURLConnection 在内部使用了 CFSocket。)

请注意,运行循环是结构化的,因此在每次迭代中只会发生以下分支之一:

  1. 就绪计时器触发,
  2. 阻止 dispatch_get_main_queue() 运行,
  3. 单个版本 1 源被分派(dispatch)到其回调。

之后,任意数量的版本 0 源都可以调用它们的回调。

所以:

  1. 布局总是发生在绘制之前,如果在 Core Animation 观察器运行时两者都处于挂起状态。 CA 观察器在计时器、主队列 block 或外部事件回调运行后运行。
  2. 主 GCD 队列的优先级高于计时器和版本 1 源,如果运行循环没有在循环的前一轮耗尽主队列。
  3. 定时器在主队列和版本 1 源上有优先权,如果这三个都准备好了的话。
  4. 主队列的优先级高于版本 1 源,两者都应该准备就绪。

另请记住,您可以随时使用 layoutIfNeeded 请求立即布局。

关于ios - iOS runloop 中的操作顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26178602/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com