Grand Central Dispatch(GCD)与performSelector的区别-需要更好的解释。
Grand Central Dispatch(GCD)与performSelector的区别-需要更好的解释。
我在我的应用程序中既使用了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命令之间有区别吗?我已经阅读了很多关于它们的文档,但还没有看到一个明确的答案。
Grand Central Dispatch (GCD) 是苹果提供的一种多线程编程的解决方案,而 performSelector 是 NSObject 类中的一个方法,用于在指定线程中执行指定的方法。然而,performSelector 方法在执行时并没有使用 GCD。
根据文档中的描述,performSelectorOnMainThread: 方法是通过调用 NSRunLoop 的 performSelector:target:withObject:order:modes: 方法来实现的。而 performSelector:target:withObject:order:modes: 方法会在下一次运行循环迭代开始时设置一个定时器,并在指定的模式下执行指定的方法。当定时器触发时,线程会尝试从运行循环中取消消息并执行方法。如果运行循环正在运行并且处于指定的模式之一中,那么执行会成功;否则,定时器会等待直到运行循环处于指定的模式之一中。
由于 performSelector 方法在内部使用了 NSRunLoop,而不是 GCD,因此可能会导致一些问题。例如,如果在主线程中使用 performSelectorOnMainThread: 方法,但是主线程的运行循环处于非指定的模式下,那么执行就会被延迟。这可能会影响用户界面的响应性能,因为用户界面通常需要在主线程上进行更新。
为了解决这个问题,可以使用 GCD 来代替 performSelector 方法。GCD 提供了更高效和更灵活的线程管理机制。通过使用 GCD,可以将任务提交到指定的队列中,而不需要关心运行循环的状态。GCD 会自动管理线程的创建和销毁,并根据系统的负载动态地调整任务的执行。
下面是一个使用 GCD 的示例代码,来替代 performSelectorOnMainThread: 方法:
dispatch_async(dispatch_get_main_queue(), ^{ // 在主线程中执行需要在主线程执行的代码 });
在上面的代码中,dispatch_async 函数会将代码块异步地提交到主队列(主线程)中执行。这样可以确保代码在主线程中执行,而不受运行循环模式的影响。
,performSelector 方法在执行时使用了 NSRunLoop,可能会导致执行延迟的问题。为了解决这个问题,可以使用 GCD 来代替 performSelector 方法,以提供更高效和更灵活的线程管理机制。
问题出现的原因是GCD的出现使得performSelector变得不再必要。GCD的方式更加高效和易于处理,而且只适用于iOS4及以后的版本。而performSelector则支持旧版本和新版本的iOS。
要解决这个问题,可以使用GCD来替代performSelector的功能。GCD提供了更强大和灵活的任务调度和并发处理的能力。
下面是一个示例代码,演示了如何使用GCD来替代performSelector的功能:
// 使用performSelector的方式 [self performSelector:@selector(doSomething) withObject:nil afterDelay:1.0]; // 使用GCD的方式 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self doSomething]; });
在上面的代码中,使用performSelector的方式会在1秒后执行doSomething方法。而使用GCD的方式,通过dispatch_after函数来实现延迟执行任务的功能。其中,dispatch_time函数用于指定延迟的时间,dispatch_get_main_queue函数用于指定任务在主线程中执行,最后通过匿名block来定义要执行的任务。
通过使用GCD来替代performSelector,我们可以更好地控制任务的调度和并发处理,提高应用程序的性能和响应速度。
GCD的出现使得performSelector变得不再必要,并且使用GCD可以更高效和灵活地处理任务调度和并发处理。通过替代performSelector的方式,我们可以提高应用程序的效率和性能。
Grand Central Dispatch (GCD)和performSelector是不同的东西,尽管它们看起来可能是一样的。事实上,它们在处理向主线程发送操作时有显著的区别,特别是当你已经在主线程上运行时。
最近我遇到了这个问题,我有一个常用的方法,有时从主线程上运行,有时不是。为了保护某些UI更新,我一直在使用-performSelectorOnMainThread:来执行它们,没有任何问题。
当我切换到在主队列上使用dispatch_sync时,每当这个方法在主队列上运行时,应用程序都会出现死锁。阅读关于dispatch_sync的文档,我们可以看到:
调用此函数并将其指向当前队列会导致死锁。
而对于-performSelectorOnMainThread:,我们可以看到:
wait
一个布尔值,指定当前线程是否在指定的选择器在主线程上执行之后阻塞。指定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函数来执行我需要在主线程上保护的操作,而不必担心原始方法是在哪个线程上执行的。
- 通过dispatch_sync()到主线程可能仍然会导致应用程序死锁,如果主线程上的某个内容正在等待来自后台线程的值(通过dispatch_sync()或其他方式)。这是非常不可能的,但仍然有可能发生的。这是两个线程互相等待对方执行某些操作的标准线程问题,所以什么都不会发生。
- 是的,如果你完全在非主队列或线程上运行,你不需要我上面的代码。我刚刚提到的死锁问题在这个代码中也会出现。这更多是一个架构问题。至于评论,参见这个Meta问题:meta.stackexchange.com/questions/43019/how-do-comment-replies-work。
你可以使用dispatch_block_t作为参数类型,而不是丑陋的void (^block)(void)。
注意事项部分说你可以比较队列的身份(你所做的)。你担心什么注意事项?
- 看起来这一部分在我写这个的时候已经更新了。它曾经告诉你对于dispatch_get_current_queue(),身份检查不能保证工作。
这一点已经添加到注意事项部分:
如果不是由dispatch_get_current_queue()返回的队列,那么同步执行到队列上是安全的,对于代码来说同样不安全。
在许多情况下,只有在主线程上不运行的情况下,才会出现异步http请求,而在响应的委托方法上执行的代码可能仍然在另一个线程上。我在调用所有我的Http请求的委托方法之前使用了这段代码,并且它救了我一天...谢谢!
使用dispatch_async(dispatch_get_main_queue(), block)会有什么问题吗?