Подтвердить что ты не робот

Подождите, пока задача async завершит завершение блока перед возвратом в делегат приложения

Я использую подкласс UIManagedDocument для использования Core Data в моем проекте. Дело в том, что подкласс возвращает экземпляр singleton, чтобы мои экраны могли просто вызвать его, а контекст управляемого объекта остался прежним для всех из них.

Перед использованием 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? Я действительно смущен.

4b9b3361

Ответ 1

Я не уверен, почему статус возврата didFinishLaunching зависит от успеха вашего обработчика завершения, поскольку вы, по-видимому, даже не рассматриваете launchOptions. Мне бы очень хотелось, чтобы вы включили синхронный вызов (или, точнее, с помощью семафора для преобразования асинхронного метода в синхронный), поскольку это замедлит работу приложения и, если оно будет достаточно медленным, вы рискуете быть убитым процесс часовой собаки.

Семафоры являются одним из распространенных способов синхронного асинхронного процесса:

- (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;
}

И вы можете получить свой контроллер просмотра addObserverForName для наблюдения за этим уведомлением.

В качестве альтернативы вы можете перенести этот код из делегата приложения и в этот контроллер представления, устраняя необходимость в уведомлении.

Ответ 2

В вашем случае использование группы отправки будет немного отличаться:

- (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;
}

Ответ 3

Множество предлагаемых решений, использующих либо dispatch_group_wait, либо семафоры, но реальное решение состоит в том, чтобы переосмыслить, почему вы хотите блокировать возврат didFinishLaunching до тех пор, пока не завершится возможно длинный асинхронный запрос. Если вы действительно не можете с пользой сделать что-нибудь еще, пока операция не завершится, моя рекомендация состояла бы в том, чтобы отобразить какую-то загрузку, пожалуйста, подождите экран, пока происходит инициализация, а затем немедленно вернется с didFinishLaunching.