dispatch_async(dispatch_get_main_queue(),^{})存在的一個坑

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    NSLog(@"before main queue : %@",[NSThread currentThread]);
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"main queue");
    });
    
    NSLog(@"after main queue : %@",[NSThread currentThread]);
    
    return YES;
}
    
2017-06-27 09:33:57.001 TestMain[1811:19444] before main queue : <NSThread: 0x60000006a080>{number = 1, name = main}
2017-06-27 09:33:57.001 TestMain[1811:19444] after main queue : <NSThread: 0x60000006a080>{number = 1, name = main}
2017-06-27 09:33:57.012 TestMain[1811:19444] main queue

可以看到在main thread中,執行順序并不是和代碼順序一樣,

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"main queue");
});

最后執行。

如果不注意的話,可能會導致一些問題。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    NSLog(@"before main queue : %@",[NSThread currentThread]);
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"main queue");
        
        self.label = [UILabel new];
    });
    
    NSLog(@"after main queue : %@",[NSThread currentThread]);
    NSLog(@"self.label: %@",self.label);
    
    return YES;
}

before main queue : <NSThread: 0x608000070ec0>{number = 1, name = main}
after main queue : <NSThread: 0x608000070ec0>{number = 1, name = main}
self.label: (null)
main queue

解決方法

  1. 參考SDWebImage的宏定義,判斷一下當前是不是主線程,如果是直接執行,如果不是,異步執行
    #ifndef dispatch_main_async_safe
    #define dispatch_main_async_safe(block)\
    if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
    block();\
    } else {\
    dispatch_async(dispatch_get_main_queue(), block);\
    }
    #endif
  1. 將需要順序的執行的代碼放到同一個作用域里面

原因

應該是和runloop有關的,但是具體的解釋還沒想明白。

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"main queue");
    
    self.label = [UILabel new];
});

dispatch_async將block提交給了queue然后立即返回,但是block什么時候執行,由queue說了算。然后,就不知道了……

更新

block 什么時候執行由Runloop說了算,不是由queue說了算,實際就是下一個runloop循環會執行,因為runloop在喚醒后會去處理相關的任務

調用信息
void
_dispatch_main_queue_callback_4CF(
        void *ignored DISPATCH_UNUSED)
{
    // the main queue cannot be suspended and no-one looks at this bit
    // so abuse it to avoid dirtying more memory

    if (_dispatch_main_q.dq_side_suspend_cnt) {
        return;
    }
    _dispatch_main_q.dq_side_suspend_cnt = true;
    _dispatch_main_queue_drain(&_dispatch_main_q);
    _dispatch_main_q.dq_side_suspend_cnt = false;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容