3

Objective-C の達人、

次のマクロを使用して、ブロックがメインスレッドで実行されるようにしています。アイデアは単純です。現在メイン スレッドを使用している場合は、すぐにブロックを実行します。現在のスレッドがメイン スレッドでない場合は、ブロックをキューに入れ、メイン スレッドで非同期に実行します (現在のスレッドをブロックしないようにします)。

これに問題はありますか?ここに安全でないもの、または私が気付いていないエラーを引き起こしているものはありますか? これを行うより良い方法はありますか?

#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;
}
4

2 に答える 2

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ブロックされていないものを使用しているため、これは安全で問題ありません

于 2013-03-20T17:12:27.403 に答える
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、ソースコードでは茶色に見えません;)

于 2013-03-20T16:59:56.623 に答える