5

同様の質問が何度か寄せられていることは知っていますが、この特定の問題を解決する方法を理解するのに苦労しています。これまでのところ、私が行ったことはすべてメイントレッドで実行されています。しばらく時間がかかる操作を実行する必要があることがわかりました。操作中に HUD をディスプレイに追加し、操作が完了するとフェードアウトしたいと考えています。

GCD について多くのことを読んだ (そしてかなり混乱した) 後、最も簡単な方法は、時間のかかるメソッドを NSInvocationOperation で呼び出し、それを新しく作成した NSOperationQueue に追加することであると判断しました。これは私が持っているものです:

        [self showLoadingConfirmation]; // puts HUD on screen

        // this bit takes a while to draw a large number of dots on a MKMapView            
        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                selector:@selector(timeConsumingOperation:)
                                                                                  object:[self lotsOfDataFromManagedObject]];

        // this fades the HUD away and removes it from the superview
        [operation setCompletionBlock:^{ [self performSelectorOnMainThread:@selector(fadeConfirmation:) withObject:loadingView waitUntilDone:YES]; }];

        NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
        [operationQueue addOperation:operation];

これにより、HUD が表示され、マップ上にドットの描画が開始され、その操作が完了すると、HUD がフェードアウトすると予想されます。

代わりに、HUD を表示し、マップ上にドットの描画を開始し、ドットを描画しながら HUD の方向にフェードアウトします。私の NSLogs によると、HUD をフェードするメソッドを呼び出す前に約 4 分の 1 秒の遅延があります。その間、ドットの描画はさらに数秒間続きます。

HUD をフェードアウトする前にマップの描画が完了するまで待機させるにはどうすればよいですか?

ありがとう

追加するために編集:

次の変更を加えた後、ほとんど成功しています。

        NSInvocationOperation *showHud = [[NSInvocationOperation alloc] initWithTarget:self
                                                                              selector:@selector(showLoadingConfirmation)
                                                                                object:nil];

        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                selector:@selector(timeConsumingOperation:)
                                                                                  object:[self lotsOfDataFromManagedObject]];

        NSInvocationOperation *hideHud = [[NSInvocationOperation alloc] initWithTarget:self
                                                                              selector:@selector(fadeConfirmation:)
                                                                                object:loadingView];

        NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];

        NSArray *operations = [NSArray arrayWithObjects:showHud, operation, hideHud, nil];
        [operationQueue addOperations:operations waitUntilFinished:YES];

奇妙なことに、最初に timeConsumingOperation を呼び出し、次に showLoadingConfirmation、次に fadeConfirmation を呼び出しているようです。これは、これらのメソッド内で起動される私の NSLogs によるものです。

画面に表示される動作は次のとおりです。ドットが描画され、それに応じてマップがズームを調整し (timeConsumingOperation の一部)、HUD が画面に表示され、その後何も表示されません。timeConsumingOperation が完了するまで showLoadingConfirmation は発生せず、fadeConfirmation はまったく発生していないように見えますが、3 つの NSLog はすべて即座に表示されます。

これは非常に奇妙に思えますが、timeConsumingOperation の完了時に何かを発生させる方法があることを示唆しているようにも見えます。

これを追加してみました:

[operationQueue setMaxConcurrentOperationCount:1];

そしてこれも:

[showHud setQueuePriority:NSOperationQueuePriorityVeryHigh];
[operation setQueuePriority:NSOperationQueuePriorityNormal];
[hideHud setQueuePriority:NSOperationQueuePriorityVeryLow];

しかし、それらは何の違いもないようです。

4

2 に答える 2

1

HUD が消えている場合は、完了ブロックが呼び出されました。これは、timeConsumingOperation:メソッドが返されたことを意味します。

それでもドットが描画されている場合は、アニメーションまたは描画トランザクションがまだ進行中か、timeConsumingOperation:返品後もキューに入れられていることを意味します。解決策は、描画に使用している手法によって異なります。コアアニメーションを使用していますか?注釈付きのMapKit?

于 2012-11-19T14:50:41.820 に答える
1

NSInvocationOperation の完了ハンドラーを設定するときは、実際に何をしているのかに注意してください。そのような操作が終了すると、次のことが起こります (NSOperation クラス リファレンスより)。

完了ブロックの正確な実行コンテキストは保証されていませんが、通常はセカンダリ スレッドです。したがって、このブロックを使用して、非常に特殊な実行コンテキストを必要とする作業を行うべきではありません。代わりに、その作業をアプリケーションのメイン スレッドまたはそれを実行できる特定のスレッドにシャントする必要があります。たとえば、操作の完了を調整するためのカスタム スレッドがある場合、完了ブロックを使用してそのスレッドに ping を実行できます。

最初に、ブロックはセカンダリ スレッドで実行されます (したがって、ブロックはそのスレッドのキューに入り、最終的に順番が来ると、別のジョブをメイン スレッドのキューに送信します)。つまり、timeConsumingOperation:セレクターからメイン キューに送信されたピンの最終更新など、他の保留中のジョブとそのようなキューに混在することになります。

ここで問題が発生します。さまざまなジョブに優先順位を設定しないと、それらのジョブが時間内に送信されたことがわかっていても、そのようなジョブが最終的に処理される順序を知る方法がありません。さらに、あなたの場合、 NSInvocationOperation が終了したという事実は、ブロックが呼び出されるまでにすべてのオブジェクトが画面に描画されていることを必ずしも意味するわけではありません。彼らの番が来る。

これを念頭に置いて、GCD の方法に行きたくないことを考えると (最初は簡単ではないことがわかっているので、もう一度試してみることをお勧めしますが、それを理解すると、それが素晴らしい解決策であることがわかります) iPhone でやりたいほとんどすべてのマルチスレッド化のために) 私は NSOperationQueue を作成し、そこにすべてのジョブを送信します (HUD を削除するジョブが含まれていますが、他のジョブよりも優先度が低くなります)。このようにして、すべての「固定」ジョブが完了した後、メイン キューで HUD の削除が確実に処理されるようにします。これが最初の目標でした。

于 2012-11-19T14:40:01.523 に答える