将选择器排入运行循环 - 是否使用[NSObject performSelector:withObject:afterDelay:]的方式?

9
我希望在当前方法执行完毕并且UI已更新后,再执行一个方法。为此,我现在使用[object performSelector:@selector(someSelector) withObject:someObject afterDelay:0.0]。根据苹果文档,这会创建一个NSTimer,然后触发并将选择器附加到当前NSRunLoop中。但我认为这不太优雅。有没有一种简单的方法可以直接将选择器排队到当前运行循环中,而不需要Cocoa创建定时器等等?
如果我在主线程上,是否可以使用performSelectorOnMainThread:withObject:waitUntilDone:或者performSelector:onThread:withObject:waitUntilDone:来减少开销并实现我的目标?
谢谢!
5个回答

6

Cocoa 是事件驱动的。你不需要在当前运行循环中“排队一个选择器”。简单来说:发送给应用程序的事件(例如用户输入、定时器、网络活动等)将导致运行循环运行,进而导致该运行周期内发生了某些事情。当然还有“细节”,但这是最基本的行为。

如果你想要推迟执行某个选择器到当前运行循环的末尾,可以将其放在最后,或者要求其在即将到来的运行循环中运行。-performSelector:...方法是正确的实现方式。它们创建一个定时器,从而产生一个事件,导致事情发生。

更多信息请参见Cocoa 事件处理指南


1
我引用的文档中写道:“在下一个运行循环周期和可选延迟期之后,在当前线程上执行指定的选择器。”这不意味着选择器必须以某种方式排队到下一个运行循环周期吗? - MrMage
当然可以。 :-) 这是通过触发计时器事件来实现的。 - Joshua Nozzi
我接受了这个,因为它给了我最多的洞察力,让我真正了解了正在发生的事情。 - MrMage

4
我不认为你强调的-performSelector:withObject:afterDelay:方法有任何不雅之处。该方法只是在当前运行循环周期完成后,将任务排队以在稍后执行。从链接到的文档中可以看到:
执行指定的选择器,即在下一个运行循环周期期间,在当前线程上并在可选延迟时间之后。因为它等待到下一个运行循环周期才执行选择器,所以这些方法提供了从当前正在执行的代码自动小延迟。多个排队的选择器按照它们被排队的顺序依次执行。
不会创建NSTimer对象来管理此操作,只需将选择器排队以在一定延迟后运行(小延迟意味着在运行循环周期完成后立即运行)。对于您希望在更新UI后发生的操作,这是最简单的技术。

如果您需要更明确的线程队列,可以查看NSOperationsNSOperationQueues。最大并发操作数为1的NSOperationQueue可以按顺序运行操作。


它确实创建了一个计时器:http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelector:withObject:afterDelay:这个方法设置了一个计时器在当前线程的运行循环上执行aSelector消息。计时器被配置为在默认模式(NSDefaultRunLoopMode)下运行。当计时器触发时,线程尝试从运行循环中出队消息并执行选择器。 - MrMage
你们两个都有一定道理。-performSelector:withObject:afterDelay:使用的是核心基础计时器API,而不是NSTimer实例。 - NSResponder
1
只是想补充一下,类似的[self performSelector:<#(SEL)#> withObject:<#(id)#>]看起来相似,但它不会等待下一次运行循环,而是立即调用方法。这是显而易见的,但我还是想提一下。 - SayeedHussain

4

我更喜欢使用NSRunLoop方法“performSelector:target:argument:order:modes:”。它保证不会在运行循环的下一次迭代之前执行选择器,并且您不必烦恼指定任意延迟等问题。


2
我自己已经多次使用过这种技术,我认为它并不那么不优雅...但是,你可以尝试另一种选择:performSelectorOnMainThread:withObject:waitUntilDone:NO。即使您已经在主线程上,也不意味着它不起作用(实际上,文档引用了从主线程调用时会发生的行为)...而且我认为当waitUntilDone设置为NO时,它将具有相同的行为,即将请求排队以执行选择器,并在当前运行循环结束时运行它。

抱歉,我的失误 - 我猜我错过了那个结尾。不过这确实是更好的方式来完成你想要做的事情。 - Kendall Helmstetter Gelner

0

仅为完整起见,我想添加这个解决方案,如果我们想要挑剔的话,这将是适当的解决方案;)

[[NSOperationQueue currentQueue] addOperationWithBlock:^{
    // will be with you in a moment...
}];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接