94

NSOperationQueue持っていますがwaitUntilAllOperationsAreFinished、同期的に待ちたくありません。キューが終了したときに UI の進行状況インジケーターを非表示にしたいだけです。

これを達成するための最良の方法は何ですか?

NSOperationどれが最後になるかわからず[queue operations]、通知を受信したときにまだ空になっていない (またはさらに悪いことに、再入力されている) 可能性があるため、自分の s から通知を送信できません。

4

15 に答える 15

167

KVOを使用しoperationsてキューのプロパティを監視し、をチェックすることでキューが完了したかどうかを確認できます[queue.operations count] == 0

KVOを実行しているファイルのどこかで、次のようにKVOのコンテキストを宣言します(詳細):

static NSString *kQueueOperationsChanged = @"kQueueOperationsChanged";

キューを設定するときは、次のようにします。

[self.queue addObserver:self forKeyPath:@"operations" options:0 context:&kQueueOperationsChanged];

次に、これをあなたのobserveValueForKeyPath:で行います

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                         change:(NSDictionary *)change context:(void *)context
{
    if (object == self.queue && [keyPath isEqualToString:@"operations"] && context == &kQueueOperationsChanged) {
        if ([self.queue.operations count] == 0) {
            // Do something here when your queue has completed
            NSLog(@"queue has completed");
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object 
                               change:change context:context];
    }
}

NSOperationQueue(これは、あなたがという名前のプロパティにいることを前提としていますqueue

オブジェクトが完全にロック解除される前のある時点で(またはキューの状態を気にするのをやめたとき)、次のようにKVOから登録を解除する必要があります。

[self.queue removeObserver:self forKeyPath:@"operations" context:&kQueueOperationsChanged];


補遺:iOS 4.0にはNSOperationQueue.operationCountプロパティがあり、ドキュメントによるとKVOに準拠しています。ただし、この回答はiOS 4.0でも機能するため、下位互換性のために引き続き役立ちます。

于 2010-04-17T03:57:34.357 に答える
20

この動作に一致するものを期待している(または望んでいる)場合:

t=0 add an operation to the queue.  queueucount increments to 1
t=1 add an operation to the queue.  queueucount increments to 2
t=2 add an operation to the queue.  queueucount increments to 3
t=3 operation completes, queuecount decrements to 2
t=4 operation completes, queuecount decrements to 1
t=5 operation completes, queuecount decrements to 0
<your program gets notified that all operations are completed>

多数の「短い」操作がキューに追加されている場合、代わりにこの動作が表示される場合があることに注意してください(操作はキューへの追加の一部として開始されるため)。

t=0  add an operation to the queue.  queuecount == 1
t=1  operation completes, queuecount decrements to 0
<your program gets notified that all operations are completed>
t=2  add an operation to the queue.  queuecount == 1
t=3  operation completes, queuecount decrements to 0
<your program gets notified that all operations are completed>
t=4  add an operation to the queue.  queuecount == 1
t=5  operation completes, queuecount decrements to 0
<your program gets notified that all operations are completed>

私のプロジェクトでは、シリアルNSOperationQueueに多数の操作が追加された後(つまり、maxConcurrentOperationCount = 1)、すべてが完了したときにのみ、最後の操作がいつ完了したかを知る必要がありました。

グーグル私は、「シリアルNSoperationQueue FIFOですか?」という質問に答えて、Apple開発者からこのステートメントを見つけました。 -

すべての操作の優先度が同じで(操作がキューに追加された後も変更されない)、すべての操作が操作キューに入れられるまでに常に--isReady == YESである場合、シリアルNSOperationQueueはFIFOです。

クリスケインココアフレームワーク、アップル

私の場合、最後の操作がいつキューに追加されたかを知ることができます。したがって、最後の操作が追加された後、優先度の低い別の操作をキューに追加します。これは、キューが空になったという通知を送信するだけです。Appleの声明を考えると、これにより、すべての操作が完了した後にのみ1つの通知のみが送信されることが保証されます。

最後の操作を検出できない方法で操作が追加されている場合(つまり、非決定論的)、上記のKVOアプローチを使用し、さらにガードロジックを追加して検出を試みる必要があると思います。操作が追加される場合があります。

:)

于 2010-11-17T18:49:15.433 に答える
18

最後に実行されるように、他のすべてに依存する NSOperation を追加するのはどうですか?

于 2009-09-07T17:11:47.303 に答える
12

1 つの代替方法は、GCD を使用することです。これを参考にしてください。

dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,queue,^{
 NSLog(@"Block 1");
 //run first NSOperation here
});

dispatch_group_async(group,queue,^{
 NSLog(@"Block 2");
 //run second NSOperation here
});

//or from for loop
for (NSOperation *operation in operations)
{
   dispatch_group_async(group,queue,^{
      [operation start];
   });
}

dispatch_group_notify(group,queue,^{
 NSLog(@"Final block");
 //hide progress indicator here
});
于 2013-05-08T10:03:57.507 に答える
5

これが私のやり方です。

キューを設定し、操作プロパティの変更を登録します。

myQueue = [[NSOperationQueue alloc] init];
[myQueue addObserver: self forKeyPath: @"operations" options: NSKeyValueObservingOptionNew context: NULL];

...そしてオブザーバー (この場合はself) は以下を実装します:

- (void) observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context {

    if (
        object == myQueue
        &&
        [@"operations" isEqual: keyPath]
    ) {

        NSArray *operations = [change objectForKey:NSKeyValueChangeNewKey];

        if ( [self hasActiveOperations: operations] ) {
            [spinner startAnimating];
        } else {
            [spinner stopAnimating];
        }
    }
}

- (BOOL) hasActiveOperations:(NSArray *) operations {
    for ( id operation in operations ) {
        if ( [operation isExecuting] && ! [operation isCancelled] ) {
            return YES;
        }
    }

    return NO;
}

この例の「スピナー」は、UIActivityIndicatorView何かが起こっていることを示しています。もちろん、着替えも可能です。...

于 2009-11-15T19:48:02.447 に答える
2

KVO を使用しoperationCountてキューのプロパティを監視するのはどうですか? 次に、キューが空になったときと、空でなくなったときにそれについて聞くでしょう。プログレス インジケータの処理は、次のように行うだけで簡単に実行できます。

[indicator setHidden:([queue operationCount]==0)]
于 2009-09-19T21:03:02.957 に答える
2

次のような最後の操作を追加します。

NSInvocationOperation *callbackOperation = [[NSInvocationOperation alloc] initWithTarget:object selector:selector object:nil];

そう:

- (void)method:(id)object withSelector:(SEL)selector{
     NSInvocationOperation *callbackOperation = [[NSInvocationOperation alloc] initWithTarget:object selector:selector object:nil];
     [callbackOperation addDependency: ...];
     [operationQueue addOperation:callbackOperation]; 

}
于 2012-10-09T19:36:08.377 に答える
2

ReactiveObjCを使用すると、これがうまく機能することがわかります。

// skip 1 time here to ignore the very first call which occurs upon initialization of the RAC block
[[RACObserve(self.operationQueue, operationCount) skip:1] subscribeNext:^(NSNumber *operationCount) {
    if ([operationCount integerValue] == 0) {
         // operations are done processing
         NSLog(@"Finished!");
    }
}];
于 2015-12-29T10:19:53.380 に答える
0

新しい を作成するNSThreadか、セレクターをバックグラウンドで実行して、そこで待機することができます。終了したらNSOperationQueue、独自の通知を送信できます。

私は次のようなことを考えています:

- (void)someMethod {
    // Queue everything in your operationQueue (instance variable)
    [self performSelectorInBackground:@selector(waitForQueue)];
    // Continue as usual
}

...

- (void)waitForQueue {
    [operationQueue waitUntilAllOperationsAreFinished];
    [[NSNotificationCenter defaultCenter] postNotification:@"queueFinished"];
}
于 2009-06-26T13:13:12.137 に答える
0

このOperationwhenEmpty {}を基本クラスとして使用する場合、ブロックをOperationQueueに渡すことができます。

let queue = OOperationQueue()
queue.addOperation(op)
queue.addOperation(delayOp)

queue.addExecution { finished in
    delay(0.5) { finished() }
}

queue.whenEmpty = {
    print("all operations finished")
}
于 2017-04-19T00:18:14.473 に答える