7

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」というセクションがあります。これを実装するのに少し問題があります。

私が望むのは、アプリケーションがキューに入れられたアイテムの処理を完了し、ユーザーにエラー メッセージを表示せずに終了することです。ビューを更新するか、シートを使用して、アプリが何らかのアクションを実行中であり、アクションが完了すると終了することをユーザーに知らせることも役立ちます。

この動作をどこでどのように実装しますか?

解決策:ここでいくつかの問題がありました。

  1. dispatch_queueアプリケーションが正常に終了するのを妨げているコアデータにアクセスしているブロックがありました。

  2. 新しい項目を 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);
    }
}
4

1 に答える 1

0

私はこれを自分で扱ったことはありませんが、ドキュメントを読んだだけで、あなたがすべきことは次のようになります。

  1. NSTerminateLaterから戻るapplicationShouldTerminate:。これにより、アプリはまだ終了する準備ができていないが、すぐに終了することをシステムに知らせます。
  2. ディスパッチ キューに「最終」ブロックを追加します。(この後、他のブロックがキューに入れられていないことを確認する必要があります。このブロックは、他のすべての作業が実行された後に実行されます。これが正しく機能するには、キューがシリアルである必要があることに注意してください。同時キューの 1 つではありません)。 .) 「最終」ブロックは[NSApp replyToApplicationShouldTerminate:YES];、通常の終了プロセスを完了する必要があります。

GCD キューがまだ機能しているかどうかを直接確認する方法はありません。これを処理するために (私が知っている) 他にできる唯一のことは、すべてのブロックをディスパッチ グループに入れ、グループを待機することですapplicationShouldTerminate:( dispatch_group_wait().

于 2012-04-09T23:55:35.743 に答える