2013-03-20 72 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