12

NSOperationの使用と描画に関するアドバイスを求めています。

メインスレッドにサブクラスを作成させ、NSOperationサブクラスに追加しますNSOperationQueue

NSOperationはいくつかの重い処理を行います.main()メソッドで数分間ループし、常にいくつかの作業を処理することを目的としていますが、今のところ、内部にsleep(1)を含むwhile()ループがあり、移動するように設定されています.約5回(テスト用)。

これを生成するメイン (元の) スレッドNSOperationは、ビューへの描画と UI の更新を担当します。

NSOperation スレッドに通知を使用して、メイン スレッドにある程度の処理が完了したことを伝えるつもりでしたが、現時点では、この通知は while() ループを通過するたびに 1 回 (つまり、1 秒に 1 回) 送信されます。 sleep(1) を実行しているだけです)。メイン スレッド (ビュー) は、これらの通知を受け取るように登録します。

通知はすぐにメイン スレッドに到達し、非同期のように見えますが、問題ないように見えます。両方のスレッドが期待どおりに実行されているようです...つまり、同時に実行されます。(各スレッドがいつ通知を送受信するかを大まかに確認するために NSLog() を使用します)。

ビューが通知を受け取り、そのハンドラー メソッドが呼び出されると、単純に整数変数をインクリメントし、これを (もちろん文字列として) ビューに描画しようとします。テストでは、drawRect: のコードは、この整数を (文字列として) 画面にうまく描画します。

ただし、ここに私の問題があります (申し訳ありませんが、ここに到達するまでに少し時間がかかりました): メインスレッド (ビュー) が NSOperation から通知を受け取ると、このテスト整数を更新し、[self setNeedsDisplay] を呼び出します。ただし、ビューは NSOperation が終了するまで再描画されません! 別のスレッドである NSOperation には、メイン スレッドのイベント ループをブロックする機能がないことを期待していましたが、これが起こっているようです。NSOperation が終了し、その main() が戻ると、ビューは最終的にすぐに再描画されます。

おそらく私はNSOperation正しく使用していません。「非並行」モードで使用していますが、名前にもかかわらず、これでも新しいスレッドが生成され、非同期処理が可能になると理解しています。

コードを見たい場合は、私に知らせてください。

4

2 に答える 2

10

通知に応じて実行されるオブザーバーのメソッドが、メイン スレッドで実行されていません。

したがって、そのメソッドでは、 を使用して別のメソッドをメイン スレッドで強制的に実行できますperformSelectorOnMainThread:withObject:waitUntilDone:

例えば:

MyOperation.m

- (void)main {
    for (int i = 1; i <= 5; i++) {
        sleep(1);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"GTCNotification" object:[NSNumber numberWithInteger:i]];
    }
}

MyViewController.m

- (void)setupOperation {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myNotificationResponse:) name:@"GTCNotification" object:nil];

    NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
    MyOperation *myOp = [[MyOperation alloc] init];

    [opQueue addOperation:myOp];

    [myOp release];
    [opQueue release];
}

- (void)myNotificationResponse:(NSNotification*)note {
    NSNumber *count = [note object];
    [self performSelectorOnMainThread:@selector(updateView:) withObject:count waitUntilDone:YES];
}

- (void)updateView:(NSNumber*)count {
    countLabel.text = count.stringValue;
}
于 2010-01-21T20:26:53.447 に答える
0

ほとんどの人は、ディスプレイ関連のタスクはメイン スレッドで実行する必要があることを知っています。ただし、その直接的な結果として、描画に影響を与える可能性のある Cocoa バインディング プロパティへの変更は、メイン スレッドでも変更する必要があります (KVO トリガーはトリガー元のスレッドで処理されるため)。これはほとんどの人にとって驚きです。特に、メイン スレッド以外のスレッドから [self setNeedsDisplay] を呼び出すことは安全ではありません。

gerry が述べたように、NSNotification はメイン スレッドでは処理されず、送信元のスレッドで処理されます。したがって、NSNotification ハンドラーでは、コマンドがディスプレイに関連する場合、または Cocoa バインディングに影響する場合は、コマンドをメイン スレッドに送り返す必要があります。@performSelector は機能しますが、キューを使用すると、より簡単で読みやすい方法が見つかりました。

[ [ NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
    /* Your main-thread code here */ }];

メイン スレッドから呼び出されることのみを目的とする 2 次ヘルパー関数の定義を回避します。mainQueue は >10.6 でのみ定義されていました。

于 2015-09-23T02:06:50.587 に答える