0

次のように NSOperationQueue のコールバックを登録しています。

[self.queue addObserver:self forKeyPath:@"operationCount" options:NSKeyValueObservingOptionNew context:NULL];

長いタスクの有効期限ハンドラーがあるため、これを operationCount のコールバックで行います。私は基本的に、キュー内の NSOperation が終了した後に状態を保存し、後で再開しようとしています。だから私はこれを行います:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"operationCount"]) {
        NSNumber *num = [change objectForKey:NSKeyValueChangeNewKey];
        self.progress = (1.0 - (double)[num integerValue] / self.totalPackets);

        if ([UIApplication sharedApplication].backgroundTimeRemaining <= MIN_BACKGROUND_TIME) {
            // Background time is almost up, save the state and resume later
            NSLog(@"running out of time");
            [self.queue cancelAllOperations];
            [self.queue removeObserver:self forKeyPath:@"operationCount" context:NULL];

            if (self.patientProcessingTaskID != UIBackgroundTaskInvalid) {
                [[UIApplication sharedApplication] endBackgroundTask:self.patientProcessingTaskID];
                self.patientProcessingTaskID = UIBackgroundTaskInvalid;
            }
        }

        if (self.queue.operationCount == 0) {
            NSLog(@"no more operations");
            [self.queue removeObserver:self forKeyPath:@"operationCount" context:NULL];

            if (self.patientProcessingTaskID != UIBackgroundTaskInvalid) {
                [[UIApplication sharedApplication] endBackgroundTask:self.patientProcessingTaskID];
                self.patientProcessingTaskID = UIBackgroundTaskInvalid;
            }
        }
    }
}

期待どおりに動作しません。コードをステップ実行すると、[self.queue removeObserver:..] が実行されることがわかります。ただし、obserValueForKeyPath: メソッドでコールバックを取得することになりますが、その理由はわかりません (self.queue のオブザーバーとして自分自身を削除したと仮定します。self を正しく削除していますか?ありがとう!

4

1 に答える 1

0

-addObserver:...本当に一度しか電話していないのですか?-addObserver:...複数回呼び出すと、複数のコールバックが返され、コールバックの受信を停止するには、への呼び出しごとに 1 回-observeValueForKeyPath:...呼び出す必要があります。-removeObserver:-addObserver:...

ここにも別の問題があります。私の経験では、 (同じ keyPath に対して) addObserver:...or removeObserver:...inを呼び出すことはトラブルの元です。observeValueForKeyPath:...「喫煙銃」スタック フレームを含むクラッシュ トレースを投稿していませんが、同じキー パスの通知中にオブザーバーを追加および削除すると、断続的なクラッシュが発生する可能性があります。特定のプロパティのオブザーバーが通知される順序は非決定的であることを経験的に観察したため、通知ハンドラー内でオブザーバーを追加または削除することが時々機能する場合でも、さまざまな状況では任意に失敗する可能性があります。(おそらくポインタの数値またはそのような任意のものに基づいて、さまざまな順序で列挙できる、KVO の内部のどこかに順序付けられていないデータ構造があると想定しています。)

GCD を使用するか-performSelector:withObject:afterDelay:、実行ループの現在の反復が完了した後に観測を削除できます。ただし、これ以上通知を受信しないという保証はありません。そのため、その保証が必要な場合は、この条件についてリスナーの状態にチェックを組み込む必要があります。

最後に、すべての KVO 遵守にはコンテキストを使用してください。詳細については、ここに投稿した説明を参照してください。

于 2013-08-03T19:34:03.667 に答える