2013-03-20 75 views
3

Objective-C大师,这个run_on_main()宏有什么问题吗?

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

您是否发现此问题?这里有什么不安全的地方,或者导致我不知道的错误?有没有更好的方法来做到这一点?

#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; 
} 
+5

为什么不使用'dispatch_async'来覆盖这两种情况?看起来很奇怪,在一种情况下,块立即执行,另一种情况下很快就会在某一时刻运行 - 您的代码无法确定它将在哪个线程上运行,而无需检查它们首先处于哪个线程,从而破坏了这一点?所以我想我试图说执行顺序是不同的,这取决于你在哪个线程,但调用代码不知道这 – 2013-03-20 17:00:22

+1

@ Paul.s - 原因很简单:如果我在MainThread上,那么我关心关于执行顺序。如果我不在主线程中,那么我只是想将块放到下一个可用位置的主线程中,而不会无意中造成死锁状态。当一个方法由于UI事件或者后台线程的网络响应而不知道它是否被调用时,这很重要。 – 2013-03-20 19:12:34

+0

如果您使用'dispatch_sync',但您的示例使用'dispatch_async',这是一种非阻塞方法 – 2013-03-20 19:14:43

回答

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

+0

在第二种情况下,最好还是15. – 2013-03-20 18:21:15

+0

@NikolaiRuhe抱歉,我不关注? – 2013-03-20 18:34:52

+0

在第二种情况下,结果取决于竞态条件。如果主队列在后台线程打印之前恰好增加'整数',结果将是15. – 2013-03-20 19:02:00

4

一如往常,如果有另一种选择,避免宏。在这种情况下,使用一个函数很容易:

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

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

加:run_on_main不会出现在你的源代码褐色;)

+0

谢谢!我会考虑做出这样的改变......这也可能对调试器有帮助,因为调试器有时无法找到正确的路线。 – 2013-03-20 19:26:54

+0

问题:我有一个“Common.h”文件包含在我的宏中,这个文件遍布整个地方。这是放置静态内联方法的最佳位置吗? – 2013-03-20 19:28:07

+0

@JohnFowler是的,这是一个放置常用功能的完美场所。由于它被声明为“静态内联”,所以无处不在。 – 2013-03-20 19:34:24