Wait for async task to finish completion block before returning in app delegate

痴心易碎 提交于 2019-11-28 03:28:42

I'm unsure why the didFinishLaunching return status is dependent upon the success of your completion handler as you're not apparently even considering launchOptions. I'd hate to see you put an synchronous call (or more accurately, use a semaphore to convert an asynchronous method into a synchronous one) here, as it will slow down the app and, if its slow enough, you risk being killed by the watch dog process.

Semaphores are one common technique for making an asynchronous process synchronous:

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

But, upon further review of what prepareWithCompletionHandler is doing, it's apparently calling methods that dispatch their own completion blocks to the main queue, so any attempts to make this synchronous will deadlock.

So, use asynchronous patterns. If you want to initiate this in the didFinishLaunchingWithOptions, you can have it post a notification:

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

And you can then have your view controller addObserverForName to observe this notification.

Alternatively, you can move this code out of the app delegate and into that view controller, eliminating the need for the notification.

Cy-4AH

For your case using dispatch group will be slightly different:

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

A lot of proposed solutions here using either dispatch_group_wait or semaphores, but the real solution is to rethink why you want to block from returning didFinishLaunching until after a possibly lengthy asynchronous request completes. If you really can't usefully do anything else until the operation completes, my recommendation would be to display some sort of a loading please wait screen while the initialization happens and then immediately return from didFinishLaunching.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!