2013-03-29 3 views
5

私が働いているアプリは、アプリケーションサーバーからのローカルデータキャッシュを定期的に更新します(10回以上のリクエスト、それぞれにかなりの時間がかかります)。私は現在、UIスレッドをブロックしないようにこれらの要求を非同期的に実行しています。これらの要求は処理に時間がかかってコアデータに読み込まれるため、beginBackgroundTaskWithExpirationHandlerと従属操作の動作をNSOperationQueueに利用したいと思います。NSOperationQueue waitUntilAllOperationsAreFinishedがバックグラウンドで動作していない

すべてのリクエストを操作キューに追加した後、すべての操作が完了するまでブロックされます(メインスレッドにはありません)。waitUntilAllOperationsAreFinishedを使用してブロックします。私のプロトタイプで見ている問題は、私がアプリケーションを実行して直ちに(ホームボタンを押す)、すべての操作が完了した後でも、waitUntilAllOperationsAreFinishedがブロックされたままになっているということです。しかし、 、ハンドラは終了する。私はアプリを実行し、それがフォアグラウンドに残るようにすると、すべてが正常に終了します。この動作は、常にとき私の実際のアプリで起きていないようですが、以下のコード例では、と思わ:

#import "ViewController.h" 

@interface ViewController() 

@property (assign, nonatomic) UIBackgroundTaskIdentifier task; 
@property (strong, nonatomic) NSOperationQueue *queue; 

@end 

@implementation ViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    [self performSelectorInBackground:@selector(queueItUp) withObject:nil]; 
} 

- (void)queueItUp { 
    UIApplication *application = [UIApplication sharedApplication]; 

    self.queue = [[NSOperationQueue alloc] init]; 
    self.task = [application beginBackgroundTaskWithExpirationHandler:^{ 
     NSLog(@"Took too long!"); 

     [self.queue cancelAllOperations]; 
     [application endBackgroundTask:self.task]; 
     self.task = UIBackgroundTaskInvalid; 
    }]; 

    for (int i = 0; i < 5; i++) { 
     [self.queue addOperationWithBlock:^{ 
      [NSThread sleepForTimeInterval:3]; 
      NSLog(@"Finished operation."); 
     }]; 
    } 


    NSLog(@"Waiting until all operations are finished."); 

    [self.queue waitUntilAllOperationsAreFinished]; 

    [application endBackgroundTask:self.task]; 
    self.task = UIBackgroundTaskInvalid; 

    NSLog(@"All done :)"); 
} 

@end 

は私が間違って何をしているのですか?

ありがとうございました

答えて

4

のコード例ではiPhoneやシミュレータ上のiOS 6.1のどの時点でも永久にブロックされません。行[application endBackgroundTask:self.task];にブレークポイントを置き、毎回それを打つでしょう。

表示されている動作は、アプリケーションにバックグラウンドタスクを終了するように指示した後、バックグラウンドでログしているためです。正確な詳細はわかりませんが、アプリケーションのフォアグラウンドへの復旧時にログがコンソールに出力されるようにキューに入れられます。代わりにスレッドの実行が中断されている可能性があります。

NSLog(@"All done");endBackgroundTask:に転送する前に移動すると、ログ出力が表示されます。

+0

ええと...ありがとう笑 – chinabuffet

1

サンプルコードを使用すると、正常に動作しています。あなたが掛けているのはNSLog(@"All done :)");です。キューはまだ存在し、保留中の操作はありませんが、アクティブなrunloopがなくなり、メインスレッドがバックグラウンドでブロックされます。保留中のバックグラウンド操作が完了したため、ブロックされます。アプリケーションを再開すると、中止された場所からアプリケーションが続行されます。これを行う場合:

[[self queue] addOperationWithBlock:^{ 
     NSLog(@"All done :)"); 
    }]; 

この動作はさらにはっきりするはずです。それはまさにあなたがすることを言っていることをやっている。

操作の束が並んでいて、waitUntilAllOperationsAreFinishedが呼び出されています。彼らが終了すると、あなたはバックグラウンドでブロックされます。

この操作のグループが完了したら、何かを実行しようとしているようです。 NSOperationは、この種の動作を構築するための依存関係と完了ブロックを持つ機能を提供します。操作をグループ化し、操作の完了時に実行される完了ブロックまたは操作を設定することができます。これの一部は、Concurrency Programming Guide

+0

ええと...私は、waitUntilAllOperationsAreFinishedをバリエーション/ゲートとして使用し、そのゲートを一度過ぎると、仕上げアクションを実行します(このケースでは、ダウンロードされたすべてのデータをコアにロードしますデータ)。その代わりに、他のすべての操作に依存する別の操作を作成し、その操作を実行することをお勧めしますか?したがって、 'waitUntilAllOperationsAreFinished'を使用する必要性を防ぐことができますか? – chinabuffet

+0

これを行うことができます。他のすべての操作が依存している単一の操作を実行できます。他の操作などが含まれる操作を実行できます。操作が並行でない場合は、操作を最後の項目としてスケジュールします。終了アクションを実行するキュー'' waitUntilAllOperationsAreFinished''を使用して、バックグラウンドにいる間にキューが確実に実行されるようにします。あなたはそれを呼び出す前にあなたの仕上げ行為(それが何であれ)を待ち行列に入れておく必要があります。 – quellish

+0

バックグラウンドタスクが既に開始されている場合、なぜ 'waitUntilAllOperationsAreFinished'を使用する必要があるのでしょうか? – chinabuffet

関連する問題