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指令是否有区别呢? 我已经阅读了很多关于它们的文档,但是还没有看到明确的答案。
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消息。 定时器被configuration为以modes参数指定的模式运行。 当定时器触发时,线程将尝试从运行循环中出列消息并执行select器。 如果运行循环正在运行并处于指定模式之一,则成功; 否则,定时器将等待,直到运行循环处于这些模式之一。
正如雅各所指出的那样,虽然它们看起来是一样的,但却是不同的东西。 实际上,如果您已经在主线程上运行,那么它们处理向主线程发送操作的方式会有很大差异。
我最近遇到了这个问题,我有一个常用的方法,有时是从主线程上运行,有时不是。 为了保护某些UI更新,我一直使用-performSelectorOnMainThread:
对于他们没有任何问题。
当我切换到在主队列上使用dispatch_sync
时,只要在主队列上运行此方法,应用程序就会死锁。 阅读dispatch_sync
的文档,我们看到:
调用此函数并将当前队列定位将导致死锁。
在哪里for -performSelectorOnMainThread:
我们看到
等待
一个布尔值,指定当前线程是否在主线程上的接收器上执行指定的select器之后阻塞。 指定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 });
执行我需要在主线程上保证的动作,而不必担心原始方法执行的线程。
GCD的方式是假设更高效和更容易处理,并且仅在iOS4以上版本中可用,而在较旧和较新的iOS中支持performSelector。