你如何安排块到下一个运行循环迭代运行?你如何安排块到下一个运行循环迭代运行?(How do you

2019-05-13 01:04发布

我希望能够执行block上的下一个运行循环迭代。 这不是那么重要的是它是否会被开头或下一个运行循环结束时执行,只是直到当前运行循环中的所有代码已经执行完毕的执行被推迟。

我知道下面不起作用,因为它被用在主运行循环交错,以我的代码可能会在下次运行循环中执行的,但它可能不会。

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

我相信下遭受同样的问题上面:

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

现在我相信下面会工作,因为它被放置在当前运行循环结束(纠正我,如果我错了),这将实际工作?

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

怎么样用一个定时器0区间? 文档状态: If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead. 这是否转化为对下一个运行循环迭代保证执行?

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

这是所有我能想到的选择,但我还是与保证下次运行循环迭代执行块(而不是调用一个方法)没有接近它不会有任何越快。

Answer 1:

你可能不知道的是,运行循环确实在每次迭代的一切。 (我是不是在我研究这个答案!)因为它发生, CFRunLoop是部分开源的CoreFoundation包 ,所以我们可以看看它到底是什么需要。 运行循环看起来大致是这样的:

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

您将需要决定这些API来使用基于您调度块上,当你需要它被调用。

例如,触摸在版本1的源处理,但如果你通过更新屏幕操作触摸,实际上没有该更新直到Core Animation的事务被提交,其发生在kCFRunLoopBeforeWaiting观察者。

现在假设你想,当你处理触摸调度块,但你想要的事务被提交后执行。

您可以添加自己的CFRunLoopObserverkCFRunLoopBeforeWaiting活动,但这种观察可能之前或核心动画的观测后运行,这取决于您指定的顺序和核心动画指定的顺序。 (核心动画当前规定200万的订单,但没有被记录,以便它可以改变。)

为了确保您的块核心动画的观测后运行,即使你的观察者核心动画的观察者之前运行,不要在你的观察者的回调直接调用该块。 相反,使用dispatch_async在这一点上的块添加到主队列。 把块上的主队列会强制运行循环,从它的“等待”马上醒来。 它将运行任何kCFRunLoopAfterWaiting观察员,然后它会耗尽主队列,届时它将运行你的块。



Answer 2:

我不相信有这将让你保证代码获取第二天事件循环依次运行任何API。 我也很好奇,为什么你需要一个没有别的环路,主要的一个特别运行提供了保障。

我还可以确认使用perforSelector:withObject:afterDelay并使用基于runloop计时器,并有功能类似的行为dispatch_async'ing上dispatch_get_main_queue()。

编辑:

其实,重新阅读您的问题后,这听起来像你只需要在当前 runloop转来完成。 如果这是真的,那么dispatch_async正是你所需要的。 事实上,所有的代码上面不作担保, 目前 runloop又将完成。



Answer 3:

罗布答案是伟大的,翔实。 我并不想取代它。

刚看完UIView的文档 ,我发现:

完成

当动画序列结束要执行的块对象。 此块没有返回值,并接受一个布尔参数,指示动画是否真正完成了完成处理程序被调用之前。 如果动画的持续时间为0时,该块在下一运行循环周期的开始时执行。 此参数可以为NULL。

所以,一个简单的解决办法是:

UIView.animate(withDuration: 0) {
    // anything
}


Answer 4:

我写我自己的NSObject的类别 ,其接受可变延迟值的基础上, 另一计算器的问题 。 通过传递的值为零,你正在有效地使上中下一个可用runloop重复代码的运行。



Answer 5:

dispatch_async上mainQueue是一个很好的建议,但它不会在下次运行的循环将其插入在回路中的电流运行运行。

为了让你后,你将需要采取传统方式的行为:

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

这也给了额外的好处是它可以使用NSObject中的cancelPreviousPerforms被取消。



文章来源: How do you schedule a block to run on the next run loop iteration?