web-dev-qa-db-ja.com

アプリのデリゲートに戻る前に、非同期タスクが完了ブロックを完了するのを待ちます

プロジェクトでコアデータを使用するために、UIManagedDocumentのサブクラスを使用しています。ポイントは、サブクラスがシングルトンインスタンスを返すことです。これにより、画面で単純にインスタンスを呼び出すことができ、管理オブジェクトコンテキストはすべてのインスタンスで同じままです。

UIManagedDocumentを使用する前に、ファイルパスが既に存在する場合は開くか、まだ存在しない場合は作成して、準備する必要があります。両方のシナリオを容易にするために、サブクラスにprepareWithCompletionHandler:という便利なメソッドを作成しました。

@implementation SPRManagedDocument

// Singleton class method here. Then...

- (void)prepareWithCompletionHandler:(void (^)(BOOL))completionHandler
{
    __block BOOL successful;

    // _exists simply checks if the document exists at the given file path.
    if (self.exists) {
        [self openWithCompletionHandler:^(BOOL success) {
            successful = success;

            if (success) {
                if (self.documentState != UIDocumentStateNormal) {
                    successful = NO;
                }
            }
            completionHandler(successful);
        }];
    } else {
        [self saveToURL:self.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
            successful = success;

            if (success) {
                if (self.documentState != UIDocumentStateNormal) {
                    successful = NO;
                }
            }
            completionHandler(successful);
        }];
    }
}

@end

私がやろうとしているのは、アプリデリゲートのdidFinishLaunchingWithOptionsでこの準備メソッドを呼び出し、最後にYESまたはNOのいずれかを返す前に完了ブロックが実行されるのを待つことです。私の現在のアプローチはうまくいきません。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    __block BOOL successful;
    SPRManagedDocument *document = [SPRManagedDocument sharedDocument];

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [document prepareWithCompletionHandler:^(BOOL success) {
            successful = success;
        }];
    });

    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    });

    return successful;
}

prepareWithCompletionHandlerを返す前に、successfulの完了ハンドラーが呼び出されるまで待つにはどうすればよいですか?私は本当に混乱しています。

34
Matthew Quiros

didFinishLaunchingを検討しているわけでもないので、launchOptionsの戻りステータスが完了ハンドラの成功に依存している理由はわかりません。ここで、同期呼び出し(または、より正確には、非同期メソッドを同期メソッドに変換するためにセマフォを使用)を行うのを嫌います。ウォッチドッグプロセス。

セマフォは、非同期プロセスを同期化するための一般的な手法の1つです。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    __block BOOL successful;
    SPRManagedDocument *document = [SPRManagedDocument sharedDocument];

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    [document prepareWithCompletionHandler:^(BOOL success) {
        successful = success;
        dispatch_semaphore_signal(semaphore);
    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return successful;
}

しかし、prepareWithCompletionHandlerが実行していることをさらにレビューすると、明らかに、独自の完了ブロックをメインキューにディスパッチするメソッドを呼び出しているため、この同期を試みるとデッドロックが発生します。

そのため、非同期パターンを使用します。 didFinishLaunchingWithOptionsでこれを開始したい場合、通知を投稿させることができます:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    __block BOOL successful;
    SPRManagedDocument *document = [SPRManagedDocument sharedDocument];

    [document prepareWithCompletionHandler:^(BOOL success) {
        successful = success;
        [[NSNotificationCenter defaultCenter] postNotificationName:kDocumentPrepared object:nil];
    }];

    return successful;
}

そして、View Controller addObserverForNameにこの通知を監視させることができます。

または、このコードをアプリデリゲートからそのView Controllerに移動して、通知の必要をなくすことができます。

44
Rob

ディスパッチグループを使用する場合は、少し異なります。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    __block BOOL successful;
    SPRManagedDocument *document = [SPRManagedDocument sharedDocument];

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [document prepareWithCompletionHandler:^(BOOL success) {
            successful = success;
            dispatch_group_leave(group);
        }];
    }];

    dispatch_group_wait(group,  DISPATCH_TIME_FOREVER);
    return successful;
}
5
Cy-4AH

dispatch_group_waitまたはセマフォですが、実際の解決策は、おそらく長い非同期要求が完了するまでdidFinishLaunchingを返すことをブロックする理由を再考することです。操作が完了するまで実際に他に何もできない場合、初期化が行われている間画面を待機してからすぐにdidFinishLaunchingから戻ってください。

4
David Berry