我在我的应用程序中使用了GCD和performSelectorOnMainThread:waitUntilDone,并且倾向于认为它们是可互换的 - 也就是说,performSelectorOnMainThread:waitUntilDone是GCD C语法的Obj-C包装器。我一直在想这两个命令是等价的:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:@selector(doit:) withObject:YES waitUntilDone:YES];
我不对吗?也就是说,performSelector *命令与GCD命令有区别吗?我已经阅读了很多关于它们的文档,但还没有看到明确的答案。
答案 0 :(得分:66)
我最近碰到了这个问题,我有一个常见的方法,有时是从主线程上的东西运行,有时不是。为了保护某些UI更新,我一直在使用-performSelectorOnMainThread:
,没有任何问题。
当我切换到在主队列上使用dispatch_sync
时,只要在主队列上运行此方法,应用程序就会死锁。阅读dispatch_sync
上的文档,我们看到:
调用此功能并定位 当前队列导致死锁。
我们看到-performSelectorOnMainThread:
的位置
等待
一个布尔值,指定是否 当前线程阻塞直到之后 指定的选择器是在 接收器在主线程上。指定 是以阻止此线程;除此以外, 指定NO以使此方法返回 立即
如果当前线程也是主线程 线程,并为此指定YES 参数,消息传递 并立即处理。
我仍然更喜欢GCD的优雅,它提供的更好的编译时检查,以及它在参数等方面的更大灵活性,所以我制作了这个小帮助函数来防止死锁:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
更新:为响应Dave Dribin指出caveats section ondispatch_get_current_queue()
,我已更改为使用上述代码中的[NSThread isMainThread]
。
然后我用
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
执行我需要在主线程上保护的操作,而不必担心原始方法在哪个线程上执行。
答案 1 :(得分:22)
performSelectorOnMainThread:
不使用GCD向主线程上的对象发送消息。
以下是documentation表示该方法的实现方式:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
在performSelector:target:withObject:order:modes:
上,文档声明:
此方法设置一个计时器,以在下一个运行循环迭代开始时在当前线程的运行循环上执行aSelector消息。定时器配置为以modes参数指定的模式运行。当计时器触发时,线程会尝试将消息从运行循环中出列并执行选择器。如果运行循环正在运行且处于指定模式之一,则成功;否则,计时器等待直到运行循环处于其中一种模式。
答案 2 :(得分:1)
GCD的方式是更高效,更容易处理,仅在iOS4以上可用,而iOS和更新版本支持performSelector。