Grand Central Dispatch (GCD) を介していくつかのプロセスを実行している Cocoa アプリケーション (Mac OS X SDK 10.7) があります。これらのプロセスは、スレッド セーフであると思われる方法 (このスレッドで使用する新しい managedObjectContext を作成する) で、一部の Core Data NSManagedObjects (非ドキュメント ベース) を操作しています。
私が抱えている問題は、ディスパッチ キューがまだ実行されている間にユーザーがアプリケーションを終了しようとしたときです。
NSApplication デリゲートは、実際に終了する前に呼び出されています。
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
「変更をマージできませんでした」というエラーが表示されます。別の managedObjectContext を介して実行されている操作がまだあるため、これは多少予想されます。次に、コア データ アプリケーションで生成されたテンプレートから NSAlert が表示されます。
Threading Programming Guideには、replyToApplicationShouldTerminate:メソッドの使用をほのめかしている「Be Aware of Thread Behavior at Quit Time」というセクションがあります。これを実装するのに少し問題があります。
私が望むのは、アプリケーションがキューに入れられたアイテムの処理を完了し、ユーザーにエラー メッセージを表示せずに終了することです。ビューを更新するか、シートを使用して、アプリが何らかのアクションを実行中であり、アクションが完了すると終了することをユーザーに知らせることも役立ちます。
この動作をどこでどのように実装しますか?
解決策:ここでいくつかの問題がありました。
dispatch_queue
アプリケーションが正常に終了するのを妨げているコアデータにアクセスしているブロックがありました。新しい項目を dispatch_queue に追加しようとすると、dispatch_queue の新しいインスタンスが新しいスレッドで開始されました。
これを解決するために私がしたことNSNotificationCenter
は、私の中で使用することでしたAppDelegate
((NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
呼び出された場所。コアデータが生成するテンプレートコードに次を追加します:
// Customize this code block to include application-specific recovery steps.
if (error) {
// Do something here to add queue item in AppController
[[NSNotificationCenter defaultCenter] postNotificationName:@"TerminateApplicationFromQueue" object:self];
return NSTerminateLater;
}
次にAppController
、通知用のオブザーバーを追加します (これを に追加しましたawakeFromNib
):
- (void)awakeFromNib {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(terminateApplicationFromQueue:) name:@"TerminateApplicationFromQueue" object:nil];
// Set initial state of struct that dispatch_queue checks to see if it should terminate the application.
appTerminating.isAppTerminating = NO;
appTerminating.isTerminatingNow = NO;
}
struct
また、ユーザーがアプリケーションを終了するかどうかを確認できる を作成しました。(上記の構造体の初期状態を設定しましたawakeFromNib
)。ステートメントのstruct
後に配置します。@synthesize
struct {
bool isAppTerminating;
bool isTerminatingNow;
} appTerminating;
ここで、アプリが正常に終了するのを妨げている長時間実行dispatch_queue
について説明します。これを最初に作成するときはdispatch_queue
、更新が必要な項目を追加するために for ループが使用されます。struct
この for ループが実行された後、アプリを終了する必要があるかどうかを確認する別のキュー アイテムを追加しました。
// Additional queue item block to check if app should terminate and then update struct to terminate if required.
dispatch_group_async(refreshGroup, trackingQueue, ^{
NSLog(@"check if app should terminate");
if (appTerminating.isAppTerminating) {
NSLog(@"app is terminating");
appTerminating.isTerminatingNow = YES;
}
});
dispatch_release(refreshGroup);
そして、通知が受信されたときに呼び出されるメソッド:
- (void)terminateApplicationFromQueue:(NSNotification *)notification {
// Struct to check against at end of dispatch_queue to see if it should shutdown.
if (!appTerminating.isAppTerminating) {
appTerminating.isAppTerminating = YES;
dispatch_queue_t terminateQueue = dispatch_queue_create("com.example.appname.terminate", DISPATCH_QUEUE_SERIAL); // or NULL
dispatch_group_t terminateGroup = dispatch_group_create();
dispatch_group_async(terminateGroup, terminateQueue, ^{
NSLog(@"termination queued until after operation is complete");
while (!appTerminating.isTerminatingNow) {
// add a little delay before checking termination status again
[NSThread sleepForTimeInterval:0.5];
}
NSLog(@"terminate now");
[NSApp replyToApplicationShouldTerminate:YES];
});
dispatch_release(terminateGroup);
}
}