Grand Central Dispatch(GCD)与performSelector - 需要更好的解释

时间:2011-03-07 20:54:59

标签: iphone objective-c multithreading ios grand-central-dispatch

我在我的应用程序中使用了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命令有区别吗?我已经阅读了很多关于它们的文档,但还没有看到明确的答案。

3 个答案:

答案 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。