主队列上的 performSelectorOnMainThread: 和 dispatch_async() 有啥区别?
Posted
技术标签:
【中文标题】主队列上的 performSelectorOnMainThread: 和 dispatch_async() 有啥区别?【英文标题】:What's the difference between performSelectorOnMainThread: and dispatch_async() on main queue?主队列上的 performSelectorOnMainThread: 和 dispatch_async() 有什么区别? 【发布时间】:2012-03-09 06:38:41 【问题描述】:我在修改线程内的视图时遇到问题。我尝试添加一个子视图,但显示需要大约 6 秒或更长时间。我终于让它工作了,但我不知道具体如何。所以我想知道它为什么起作用以及以下方法之间有什么区别:
-
这很有效 - 立即添加了视图:
dispatch_async(dispatch_get_main_queue(), ^
//some UI methods ej
[view addSubview: otherView];
-
这需要大约 6 秒或更长时间才能显示:
[viewController performSelectorOnMainThread:@selector(methodThatAddsSubview:) withObject:otherView
waitUntilDone:NO];
NSNotification
方法 - 还花了大约 6 秒时间来显示观察者在我想修改的 viewController 中,并与添加子视图的方法配对。
[[NSNotificationCenter defaultCenter] postNotificationName:
@"notification-identifier" object:object];
作为参考,这些是在 ACAccountStore
类的 CompletionHandler
中调用的。
accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error)
if(granted)
// my methods were here
【问题讨论】:
当你说performSelectorOnMainThread:
不起作用时,它是怎么失败的?你收到错误信息了吗?是运行时错误还是编译错误?如果你没有收到错误,你怎么知道它失败了?
addSubview:
是您唯一使用的接触 UI 元素的方法,还是还有其他方法?
@AndrewMadsen 我忘了说它有效,但显示需要大约 6 秒或更长时间。
@MattWilding 是的。在我的测试过程中,我尝试添加一个空白子视图,但仍然需要大约 6 秒才能在主线程上显示 nsnotification 和 performselector。
【参考方案1】:
默认情况下,-performSelectorOnMainThread:withObject:waitUntilDone:
仅调度选择器以默认运行循环模式运行。如果运行循环处于另一种模式(例如跟踪模式),它不会运行,直到运行循环切换回默认模式。您可以使用变体 -performSelectorOnMainThread:withObject:waitUntilDone:modes:
解决此问题(通过传递您希望它运行的所有模式)。
另一方面,dispatch_async(dispatch_get_main_queue(), ^ ... )
将在主运行循环将控制流返回给事件循环后立即运行该块。它不关心模式。因此,如果您也不想关心模式,dispatch_async()
可能是更好的选择。
【讨论】:
什么是模式,什么时候应该关注它们? @MattDiPasquale:在 ios 上,你基本上可以忽略它们,runloop 通常总是在默认模式下运行。在 OS X 上,您可能会看到另外 3 种模式,NSConnectionReplyMode
、NSModalPanelRunLoopMode
和 NSEventTrackingRunLoopMode
。如果您有兴趣,可以查找这些文档。
请注意,在 iOS 中 UITrackingRunLoopMode 用于某些触摸事件跟踪(当 UIScrollView 正在跟踪触摸移动时)。
这不是真的,它使用了默认的运行循环模式。 performSelectorOnMainThread 是这样记录的:“此方法使用 常见的运行循环模式 - 在主线程的运行循环上排队消息也就是与 NSRunLoopCommonModes constant相关的模式。"
如果您在主线程 dispatch_async 块中启动模型对话窗口,则在 OS 10.9 上通过鼠标滚轮滚动将不起作用(可能 b/c 仅在默认运行循环模式下有效)。此外,未捕获的异常将始终终止程序,这在生产代码中很糟糕(唯一的出路是在您编写的每个 dispatch_async-block 中嵌套一个 try-catch)。由于这些原因,我们更喜欢 performSelectorOnMainThread。我们创建了一个内部调用 performSelectorOnMainThread 的 dispatch_async_main 变体,因此我们可以继续使用漂亮的块语法。【参考方案2】:
这可能是因为performSelectorOnMainThread:withObject:waitUntilDone:
使用常见的运行循环模式将消息排队。根据Apple's Concurrency Programming Guide,主队列会将排队的任务与来自应用程序运行循环的其他事件交错。因此,如果事件队列中还有其他事件需要处理,调度队列中的排队块可能会先运行,即使它们是稍后提交的。
This article 是对performSelectorOnMainThread
与dispatch_async
的绝佳解释,它也回答了上述问题。
【讨论】:
【参考方案3】:你试过PerformSelectorOnMainThread
和waitUntilDone=YES
例如:
代码:
[viewController performSelectorOnMainThread:@selector(methodThatAddsSubview:) withObject:otherView waitUntilDone:YES];
我认为这可能会解决为什么 PerformSelectorOnMainThread
需要这么长时间才能回复的问题。
【讨论】:
以上是关于主队列上的 performSelectorOnMainThread: 和 dispatch_async() 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
主队列上的 dispatch_sync 与 dispatch_async