48

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

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


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

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

4

3 に答える 3

70

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

私は最近これに遭遇しました。そこでは、メインスレッド上の何かから実行されることもあれば、実行されないこともある一般的なメソッドがありました。特定のUIアップデートを保護するために、私は-performSelectorOnMainThread:問題なくそれらを使用していました。

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

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

どこで-performSelectorOnMainThread:見るか

待つ

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

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

私はまだGCDの優雅さ、それが提供するより良いコンパイル時チェック、そして引数などに関するより大きな柔軟性を好みます。そこで私はデッドロックを防ぐためにこの小さなヘルパー関数を作りました:

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

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

次に使用します

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

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

于 2011-03-07T22:53:46.723 に答える
22

performSelectorOnMainThread:メインスレッド上のオブジェクトにメッセージを送信するためにGCDを使用しません。

メソッドが実装されているとドキュメントに記載されている方法は次のとおりです。

- (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パラメーターで指定されたモードで実行するように構成されています。タイマーが起動すると、スレッドは実行ループからメッセージをデキューしてセレクターを実行しようとします。実行ループが実行中で、指定されたモードの1つである場合、成功します。それ以外の場合、タイマーは実行ループがこれらのモードの1つになるまで待機します。

于 2011-03-07T20:57:05.447 に答える
2

GCDの方法は、より効率的で扱いやすいと想定されており、iOS4以降でのみ使用できますが、performSelectorは古いiOSと新しいiOSでサポートされています。

于 2011-03-07T23:12:54.310 に答える