你如何安排一个块在下一次运行循环迭代中运行? [英] How do you schedule a block to run on the next run loop iteration?

查看:25
本文介绍了你如何安排一个块在下一次运行循环迭代中运行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够在下一次运行循环迭代中执行 block.它是在下一个运行循环的开始还是结束执行并不那么重要,只是将执行推迟到当前运行循环中的所有代码执行完毕.

I want to be able to execute a block on the next run loop iteration. It's not so important whether it gets executed at the beginning or the end of the next run loop, just that execution is deferred until all code in the current run loop has finished executing.

我知道以下内容不起作用,因为它与主运行循环交错,因此我的代码可能会在下一个运行循环中执行,但可能不会.

I know the following doesn't work because it gets interleaved with the main run loop so my code might execute on the next run loop but it might not.

dispatch_async(dispatch_get_main_queue(),^{
    //my code
});

以下我认为遇到与上述相同的问题:

The following I believe suffers the same problem as above:

dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void){
    //my code
});

现在我相信以下内容会起作用,因为它被放置在当前运行循环的末尾(如果我错了,请纠正我),这真的有效吗?

Now I believe the following would work as it is placed at the end of the current run loop (correct me if I'm wrong), would this actually work?

[self performSelector:@selector(myMethod) withObject:nil afterDelay:0];

具有 0 间隔的计时器怎么样?文档说明:如果 seconds 小于或等于 0.0,则此方法选择 0.1 毫秒的非负值. 这是否转化为保证在下一次运行循环迭代中执行?

What about a timer with a 0 interval? The documentation states: If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead. Does this translate to guaranteeing execution on the next run loop iteration?

[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(myMethod) userInfo:nil repeats:NO];

这是我能想到的所有选项,但我仍然离在下一次运行循环迭代中执行块(而不是调用方法)更近一步,并保证不会更早执行.

That's all the options I can think of but I'm still no closer to executing a block (as opposed to calling a method) on the next run loop iteration with the guarantee that it won't be any sooner.

推荐答案

您可能不知道运行循环在每次迭代中所做的一切.(在我研究这个答案之前我不是!)碰巧的是,CFRunLoop开源 CoreFoundation 包,所以我们可以看看它到底包含什么.运行循环大致如下所示:

You might not be aware of everything that the run loop does in each iteration. (I wasn't before I researched this answer!) As it happens, CFRunLoop is part of the open-source CoreFoundation package, so we can take a look at exactly what it entails. The run loop looks roughly like this:

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;
}

可以看到有多种方法可以钩入运行循环.你可以创建一个 CFRunLoopObserver 来调用你想要的任何活动".您可以创建一个版本 0 CFRunLoopSource 并立即发出信号.您可以创建一对连接的 CFMessagePorts,将其中一个包装在版本 1 CFRunLoopSource 中,然后向其发送消息.您可以创建一个 CFRunLoopTimer.您可以使用 dispatch_get_main_queueCFRunLoopPerformBlock 对块进行排队.

You can see that there are a variety of ways to hook into the run loop. You can create a CFRunLoopObserver to be called for any of the "activities" you want. You can create a version 0 CFRunLoopSource and signal it immediately. You can create a connected pair of CFMessagePorts, wrap one in a version 1 CFRunLoopSource, and send it a message. You can create a CFRunLoopTimer. You can queue blocks using either dispatch_get_main_queue or CFRunLoopPerformBlock.

您需要根据调度块的时间以及需要调用它的时间来决定使用这些 API 中的哪些.

You will need to decide which of these APIs to use based on when you are scheduling the block, and when you need it to be called.

例如,在版本 1 源中处理触摸,但是如果您通过更新屏幕来处理触摸,则在提交 Core Animation 事务之前不会实际执行该更新,这发生在 kCFRunLoopBeforeWaiting 观察者.

For example, touches are handled in a version 1 source, but if you handle the touch by updating the screen, that update isn't actually performed until the Core Animation transaction is committed, which happens in a kCFRunLoopBeforeWaiting observer.

现在假设您想在处理触摸时调度块,但希望在事务提交后执行.

Now suppose you want to schedule the block while you're handling the touch, but you want it to be executed after the transaction is committed.

您可以为 kCFRunLoopBeforeWaiting 活动添加您自己的 CFRunLoopObserver,但此观察者可能会在 Core Animation 的观察者之前或之后运行,具体取决于您指定的顺序和 Core 的顺序动画指定.(Core Animation 目前指定了 2000000 的订单,但未记录在案,因此可能会更改.)

You can add your own CFRunLoopObserver for the kCFRunLoopBeforeWaiting activity, but this observer might run before or after Core Animation's observer, depending on the order you specify and the order Core Animation specifies. (Core Animation currently specifies an order of 2000000, but that is not documented so it could change.)

为了确保您的块在 Core Animation 的观察者之后运行,即使您的观察者在之前 Core Animation 的观察者运行,也不要直接在观察者的回调中调用该块.相反,此时使用 dispatch_async 将块添加到主队列.将块放在主队列上将迫使运行循环立即从它的等待"中唤醒.它将运行任何 kCFRunLoopAfterWaiting 观察者,然后它会排空主队列,此时它将运行您的块.

To make sure your block runs after Core Animation's observer, even if your observer runs before Core Animation's observer, don't call the block directly in your observer's callback. Instead, use dispatch_async at that point to add the block to the main queue. Putting the block on the main queue will force the run loop to wake up from its "wait" immediately. It will run any kCFRunLoopAfterWaiting observers, and then it will drain the main queue, at which time it will run your block.

这篇关于你如何安排一个块在下一次运行循环迭代中运行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆