web-dev-qa-db-ja.com

Grand Central Dispatch(GCD)とperformSelector-より詳しい説明が必要

私はアプリでGCDとperformSelectorOnMainThread:waitUntilDoneの両方を使用しており、それらは交換可能であると考える傾向があります。つまり、performSelectorOnMainThread:waitUntilDoneはGCD C構文のObj-Cラッパーです。私はこれら2つのコマンドを同等のものと考えてきました。

dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });


[self performSelectorOnMainThread:@selector(doit:) withObject:YES waitUntilDone:YES];

私は間違っていますか?つまり、performSelector *コマンドとGCDコマンドの違いはありますか?私はそれらに関する多くのドキュメントを読みましたが、決定的な答えはまだありません。

47
akaru

performSelectorOnMainThread:しないGCDを使用して、メインスレッド上のオブジェクトにメッセージを送信します。

documentation がメソッドの実装方法を示しています。

- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
  [[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}

そしてperformSelector:target:withObject:order:modes:、ドキュメントは次のように述べています:

このメソッドは、次の実行ループ反復の開始時に現在のスレッドの実行ループでaSelectorメッセージを実行するタイマーを設定します。タイマーは、modesパラメーターで指定されたモードで実行するように構成されています。タイマーが起動すると、スレッドは実行ループからメッセージをデキューしてセレクターを実行しようとします。実行ループが実行されていて、指定されたモードのいずれかである場合、成功します。それ以外の場合、タイマーは、実行ループがこれらのモードのいずれかになるまで待機します。

22
Jacob Relkin

ジェイコブが指摘するように、それらは同じように見えるかもしれませんが、それらは異なるものです。実際、メインスレッドで既に実行している場合、メインスレッドへのアクションの送信を処理する方法には大きな違いがあります。

私は最近これに遭遇しましたが、メインスレッドの何かから実行されることもあれば、そうでないこともある共通のメソッドがありました。特定のUI更新を保護するために、私は_-performSelectorOnMainThread:_を問題なく使用していました。

メインキューで_dispatch_sync_を使用するように切り替えると、このメソッドがメインキューで実行されると、アプリケーションがデッドロックしました。 _dispatch_sync_ に関するドキュメントを読むと、次のようになります。

この関数を呼び出して現在のキューをターゲットにすると、デッドロックが発生します。

ここで _-performSelectorOnMainThread:_ の場合

待機

メインスレッドのレシーバーで指定されたセレクターが実行されるまで現在のスレッドがブロックするかどうかを指定するブール値。このスレッドをブロックするにはYESを指定します。それ以外の場合は、NOを指定して、このメソッドがすぐに戻るようにします。

現在のスレッドがメインスレッドでもあり、このパラメーターにYESを指定すると、メッセージはすぐに配信され、処理されます。

私は今でもGCDの優雅さ、GCDが提供するコンパイル時のチェックの向上、引数などに関する柔軟性の高さを好みます。そのため、デッドロックを防ぐためにこの小さなヘルパー関数を作成しました。

_void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}
_

Update:Dave Dribinが 警告セクションondispatch_get_current_queue() を指摘するのに応じて、次のように変更しました上記のコードで_[NSThread isMainThread]_を使用します。

次に使用します

_runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});
_

元のメソッドが実行されたスレッドを気にせずに、メインスレッドで保護する必要があるアクションを実行します。

66
Brad Larson

GODの方法は、より効率的で扱いやすいとされており、iOS 4以降でのみ使用できます。一方、performSelectorは、新旧のiOSでサポートされています。

1
Seyther