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

发布于 2024-10-20 15:15:21 字数 426 浏览 0 评论 0原文

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

I've used both GCD and performSelectorOnMainThread:waitUntilDone in my apps, and tend to think of them as interchangeable--that is, performSelectorOnMainThread:waitUntilDone is an Obj-C wrapper to the GCD C syntax. I've been thinking of these two commands as equivalent:

dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });


[self performSelectorOnMainThread:@selector(doit:) withObject:YES waitUntilDone:YES];

Am I incorrect? That is, is there a difference of the performSelector* commands versus the GCD ones? I've read a lot of documentation on them, but have yet to see a definitive answer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

漫雪独思 2024-10-27 15:15:21

正如雅各布指出的,虽然它们可能看起来相同,但它们是不同的东西。事实上,如果您已经在主线程上运行,则它们处理向主线程发送操作的方式存在显着差异。

我最近遇到了这个问题,我有一个通用方法,有时从主线程上的某个东西运行,有时则不运行。为了保护某些 UI 更新,我一直在使用 -performSelectorOnMainThread: ,没有出现任何问题。

当我切换到在主队列上使用 dispatch_sync 时,只要在主队列上运行此方法,应用程序就会死锁。阅读有关 dispatch_sync,我们看到:

调用此函数并定位
当前队列导致死锁。

其中 -performSelectorOnMainThread: 我们看到

等等

一个布尔值,指定是否
当前线程阻塞直到
指定的选择器执行
主线程上的接收器。指定
YES 阻止该线程;否则,
指定 NO 以使该方法返回
立即。

如果当前线程也是主线程
线程,并且您为此指定 YES
参数,消息传递
并立即处理。

我仍然更喜欢 GCD 的优雅,它提供的更好的编译时检查,以及它在参数方面更大的灵活性等,所以我做了这个小辅助函数来防止死锁:

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

更新: Dave Dribin 指出了注意事项部分dispatch_get_current_queue(),我在上面的代码中改为使用[NSThread isMainThread]

然后,我用它

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

来执行需要在主线程上保护的操作,而不必担心原始方法是在哪个线程上执行的。

As Jacob points out, while they may appear the same, they are different things. In fact, there's a significant difference in the way that they handle sending actions to the main thread if you're already running on the main thread.

I ran into this recently, where I had a common method that sometimes was run from something on the main thread, sometimes not. In order to protect certain UI updates, I had been using -performSelectorOnMainThread: for them with no problems.

When I switched over to using dispatch_sync on the main queue, the application would deadlock whenever this method was run on the main queue. Reading the documentation on dispatch_sync, we see:

Calling this function and targeting
the current queue results in deadlock.

where for -performSelectorOnMainThread: we see

wait

A Boolean that specifies whether the
current thread blocks until after the
specified selector is performed on the
receiver on the main thread. Specify
YES to block this thread; otherwise,
specify NO to have this method return
immediately.

If the current thread is also the main
thread, and you specify YES for this
parameter, the message is delivered
and processed immediately.

I still prefer the elegance of GCD, the better compile-time checking it provides, and its greater flexibility regarding arguments, etc., so I made this little helper function to prevent deadlocks:

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

Update: In response to Dave Dribin pointing out the caveats section ondispatch_get_current_queue(), I've changed to using [NSThread isMainThread] in the above code.

I then use

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

to perform the actions I need to secure on the main thread, without worrying about what thread the original method was executed on.

做个ˇ局外人 2024-10-27 15:15:21

performSelectorOnMainThread:使用GCD向主线程上的对象发送消息。

以下是 文档说该方法已实现:

- (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参数指定的模式下运行。当计时器触发时,线程尝试将消息从运行循环中出队并执行选择器。如果运行循环正在运行并且处于指定模式之一,则成功;否则,计时器将等待,直到运行循环处于其中一种模式。

performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.

Here's how the documentation says the method is implemented:

- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
  [[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}

And on performSelector:target:withObject:order:modes:, the documentation states:

This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.

伤痕我心 2024-10-27 15:15:21

GCD 的方式被认为更高效、更容易处理,并且仅在 iOS4 及以上版本中可用,而performSelector 在旧版和新版 iOS 中均受支持。

GCD's way is suppose to be more efficient and easier to handle and is only available in iOS4 onwards whereas performSelector is supported in the older and newer iOS.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文