4

NSOperationQueueクラスのバックグラウンドをサポートするための有用な戦略を練り上げることにいくつか問題があります。NSOperation特に、次のアクションを実行するがたくさんあります。

  • Webからファイルをダウンロードする
  • ファイルを解析する
  • CoreDataにデータファイルをインポートする

操作はシリアルキューに挿入されます。操作が完了すると、次の操作を開始できます。

アプリがバックグラウンドに入ったときに操作を停止(または続行)する必要があります。これらの議論から(AFNetworkingにはバックグラウンドサポートがありますか?およびNSOperationsのキューとアプリケーション出口の処理)、操作をキャンセルし、isCancelled各操作内でプロパティを使用するのが最善の方法だと思います。次に、操作のキーポイントをそのプロパティと照合して、アプリがバックグラウンドに入ったときに(実行中の操作の)実行の状態をロールバックできます。

バックグラウンドサポートを強調するAppleテンプレートに基づいて、どのように同様の状況を管理できますか?単に操作をキャンセルするか、現在の操作が完了するのを待つことはできますか?詳細についてはコメントを参照してください。

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithExpirationHandler:^{

        // Do I have to call -cancelAllOperations or
        // -waitUntilAllOperationsAreFinished or both?

        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // What about here?

        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    });
}

前もって感謝します。

編集

NSOperation mainメソッドが以下のコードを実行する場合、キャンセルイベントへの応答パターンに従うことはどのように可能ですか?

- (void)main
{
    // 1- download

    // 2- parse
    // 2.1 read file location
    // 2.2 load into memory

    // 3- import
    // 3.1 fetch core data request
    // 3.2 if data is not present, insert it (or update)
    // 3.3 save data into persistent store coordinator
}

私が説明した各メソッドには、さまざまなステップが含まれています(ダウンロードを除く非アトミック操作)。したがって、キャンセルはこれらの各ステップ内で発生する可能性があります(事前定義されていない方法で)。isCancelled各ステップの前にプロパティを確認できますか?これは機能しますか?

TammoFreeseの編集に基づいて編集2

編集コードの意味を理解しました。でも気になるのは次のとおりです。キャンセル要求(ユーザーはホームボタンを押すことができます)は実行中の任意の時点で発生する可能性があるmainため、単に戻ると、操作の状態が破損します。戻る前に状態をクリーンアップする必要がありますか?どう思いますか?

私が説明した問題は、同期操作(実行しているのと同じスレッド内で同期して実行される操作)を使用すると発生する可能性があります。たとえば、mainがファイルをダウンロードしていて(ダウンロードはを介して実行されます+sendSynchronousRequest:returningResponse:error)、アプリがバックグラウンドに置かれている場合、どうなる可能性がありますか?そのような状況をどのように管理するのですか?

// download
if ([self isCancelled])
    return;

// downloading here <-- here the app is put in background

もちろん、アプリをフォアグラウンドにすると、キャンセルされたので操作をやり直すと思います。言い換えれば、それはその状態を維持しないことを余儀なくされています。私が間違っている?

4

1 に答える 1

15

私があなたを正しく理解しているなら、あなたは持っています、NSOperationQueueそしてあなたのアプリケーションがバックグラウンドに入ったら、あなたは

  1. すべての操作をキャンセルし、
  2. キャンセルが処理されるまで待ちます。

通常、これにはそれほど時間はかからないので、これを行うだけで十分です。

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [_queue cancelAllOperations];
    [_queue waitUntilAllOperationsAreFinished];
}

ここでの「時間がかかりすぎる」の定義は約5秒です。-applicationDidEnterBackground:それより長くブロックすると、アプリは終了し、メモリから削除されます。

キャンセルされた操作の完了に5秒以上かかるとします。次に、バックグラウンドで待機する必要があります(説明についてはコメントを参照してください)。

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        // If this block is called, our background time of normally 10 minutes
        // is almost exceeded. That would mean one of the cancelled operations
        // is not finished even 10 minutes after cancellation (!).
        // This should not happen.
        // What we do anyway is tell iOS that our background task has ended,
        // as otherwise our app will be killed instead of suspended.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Normally this one is fast, so we do it outside the asynchronous block.
    [_queue cancelAllOperations];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Wait until all the cancelled operations are finished.
        [_queue waitUntilAllOperationsAreFinished];

        // Dispatch to the main queue if bgTask is not atomic
        dispatch_async(dispatch_get_main_queue(), ^{
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        });
    });
}

つまり、基本的には、タスクを実行するのに時間が必要であることをiOSに通知し、タスクが終了すると、タスクが終了したことをiOSに通知します。

編集

編集の質問に答えるには:キャンセルに応答するには、可能な限りキャンセルを確認し、-mainメソッドから戻ります。キャンセルされた操作はすぐには終了しませんが、-main戻ったときに終了します。

- (void)main
{
    // 1- download
    if ([self isCancelled]) return;

    // 2- parse
    // 2.1 read file location

    // 2.2 load into memory
    while (![self isCancelled] && [self hasNextLineToParse]) {
        // ...
    }

    // 3- import

    // 3.1 fetch core data request
    if ([self isCancelled]) return;


    // 3.2 if data is not present, insert it (or update)
    // 3.3 save data into persistent store coordinator
}

でキャンセルフラグをまったくチェックしない場合-main、操作はキャンセルに反応しませんが、終了するまで実行されます。

編集2

操作がキャンセルされた場合、isCancelledフラグがtrueに設定されていることを除いて、操作は何も起こりません。私の元の回答の上記のコードは、操作が終了するまでバックグラウンドで待機します(キャンセルに反応するか、キャンセルに10分もかからないと仮定して単に終了します)。

もちろん、isCancelled私たちの操作で反応するときは、たとえば、ダウンロード直後(データを無視するだけ)、またはすべてのデータを書き込んだ後など、操作を破損していない状態のままにしておく必要があります。

確かに、操作がキャンセルされたが、フォアグラウンドに戻ったときに実行されている場合、その操作はダウンロードを終了し、(そのようにプログラムした場合)キャンセルに反応して、基本的にダウンロードされたデータを破棄します。

代わりにできることは、操作をキャンセルせずに、操作が完了するのを待つことです(10分未満かかると仮定します)。これを行うには、行を削除するだけ[_queue cancelAllOperations];です。

于 2012-11-12T15:21:48.087 に答える