我在后台线程上有一些计算工作,之后我需要更新一些calayer的转换,我试试用
dispatch_async(dispatch_get_main_queue(), ^{calayer.transform = newTransform});
和
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^(void) {calayer.transform = newTransform});
我只是觉得他们是一样的, 但是当我使用dispatch_async时,我发现calayer工作得很顺利(可能?)。 这两个功能有什么不同?
答案 0 :(得分:13)
这里的主要区别是CFRunLoopPerformBlock
允许您指定执行块的特定运行循环模式,而dispatch_async(dispatch_get_main_queue(),...)
仅在常见模式下执行。也许更适合您所看到的性能问题,CFRunLoopPerformBlock
不唤醒主线程。来自CFRunLoopPerformBlock
的文档:
此方法仅将块排入队列,不会自动唤醒 指定的运行循环。因此,执行块发生了 下次运行循环唤醒以处理另一个输入源。如果你 想要立即完成工作,你必须明确地唤醒它 线程使用CFRunLoopWakeUp函数。
实际上,这通常意味着你的块不会被执行,直到运行循环唤醒(即用户事件发生,定时器触发,运行循环源触发,接收到mach消息等)GCD不是,通过设计,基于运行循环的API;主队列和主线程运行循环之间的关系实际上是一个实现细节。我希望实现唤醒运行循环本身,如果主队列需要服务那么。
如果没有相反的信息,我强烈怀疑这是性能差异的根源。如果您在致电CFRunLoopWakeUp
后立即致电CFRunLoopPerformBlock
,我预计效果会相似。
答案 1 :(得分:3)
GCD的主队列是一个串行队列。因此,它一次只能运行一个任务。即使该任务运行内部运行循环 - 例如,运行模式对话框 - 然后提交到主队列的其他任务也无法运行,直到完成。
使用CFRunLoopPerformBlock()
提交的任务可以在其中一个目标模式下运行运行循环时运行。这包括运行循环是否在使用CFRunLoopPerformBlock()
提交的任务中运行。
请考虑以下示例:
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
printf("outer task milestone 1\n");
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
printf("inner task\n");
});
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("outer task milestone 2\n");
});
产生如下输出:
outer task milestone 1
inner task
outer task milestone 2
虽然这个:
dispatch_async(dispatch_get_main_queue(), ^{
printf("outer task milestone 1\n");
dispatch_async(dispatch_get_main_queue(), ^{
printf("inner task\n");
});
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
printf("outer task milestone 2\n");
});
产生
outer task milestone 1
outer task milestone 2
inner task
答案 2 :(得分:1)
我有时会一起使用它们:
dispatch_async(dispatch_get_main_queue(), ^(void) {
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^{
// stuff
});
});
我使用它将一个块发送到主线程,该主线程将执行而不会导致'故障'当UIScrollview滚动时。
我最近也在使用:
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^{
// stuff
});
代替:
[self performSelector:@selector(myMethod:) withObject:myObject afterDelay:0]
为了将代码的执行推迟到下一次通过runloop传递。这样我就不必创建一个特殊的方法来包含我想要执行的代码,也不必将所有要执行的参数包装到单个(id)myObject中。
答案 3 :(得分:0)
可能导致严重麻烦的一种微妙之处是由于“发送”到CFRunLoopPerformBlock
的CF块中存在深层错误,并非总是按照它们发送的顺序来调用。如果订单对您的程序很重要,那么您需要找到一种自行提供订单的方法。因此,CFRunLoopPerformBlock
的文档以及另一个答案中提到的嵌套都不应使用队列术语。不幸的是,此错误也悄悄传播到NSRunLoop
的某些方法中(因为它们当然会调用CFRunLoopPerformBlock
)。