这个run_on_main()宏有什么问题吗?

时间:2013-03-20 16:54:27

标签: ios objective-c grand-central-dispatch

Objective-C Gurus,

我一直在使用以下宏来确保在主线程上运行块。这个想法很简单:如果我目前在主线程上,那么我将立即运行该块。如果当前线程不是主线程,那么我将块排队以异步方式在主线程上运行(这样它就不会阻塞当前线程)。

你觉得这有什么问题吗?这里有什么不安全的,或导致我不知道的错误?有没有更好的方法呢?

#define run_on_main(blk)    if ([NSThread isMainThread]) { blk(); } else { dispatch_async(dispatch_get_main_queue(), blk); }

示例用法:

-(BOOL)loginCompletedSuccessfully
{
    NSLog(@"loginCompletedSuccessfully");
    //  This may be called from a network thread, so let's
    //  ensure the rest of this is running on the main thread.
    run_on_main(^{
        if (_appStartupType == AppLaunch) {
            self.storyboard = [UIStoryboard storyboardWithName:DEVICED(@"XPCStoryboard") bundle:nil];
            self.navigationController = [storyboard instantiateInitialViewController];
        }
        [self.window setRootViewController:self.navigationController];
    });
    return YES;
}

2 个答案:

答案 0 :(得分:5)

这可能会导致执行顺序出现令人讨厌的微妙错误。

以这个简单的例子(在主线程上)

__block NSInteger integer = 5;

run_on_main(^{
  integer += 10;
});

NSLog(@"From Main %d", integer);

这将打印结果15

相同的代码在后台线程中运行

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
  __block NSInteger integer = 5;

  run_on_main(^{
    integer += 10;
  });

  NSLog(@"From background %d", integer);
});

结果将是5 ...或15,具体取决于主题之间的竞争条件。

这种不一致可能会让你失望。

为什么不在两种情况下都使用dispatch_async,并且知道两者现在都会表现出相同的行为。这是安全和正常的,因为您正在使用非阻塞的async

答案 1 :(得分:4)

与往常一样,如果有其他选项,请避免使用宏。在这种情况下,很容易使用函数:

static inline void run_on_main(dispatch_block_t block)
{
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

这相当于你的宏定义;你甚至可以把它放在同一个地方。优点是您可以获得编译器支持语法检查,块的Xcode语法完成(非常有用),调试时支持调试器等等。

Plus:run_on_main在源代码中不显示为棕色;)