ios 在应用程序委托中返回之前等待异步任务完成完成块

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22914753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:55:38  来源:igfitidea点击:

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

iosobjective-cgrand-central-dispatch

提问by Matthew Quiros

I'm using a subclass of UIManagedDocumentto use Core Data in my project. The point is for the subclass to return a singleton instance so that my screens can simply call it and the managed object context remains the same for all of them.

我正在使用的子类UIManagedDocument在我的项目中使用 Core Data。关键是让子类返回一个单例实例,以便我的屏幕可以简单地调用它,并且托管对象上下文对所有这些都保持相同。

Before using the UIManagedDocument, I need to prepare it by opening it if its file path already exists, or creating it if it doesn't yet. I created a convenience method prepareWithCompletionHandler:in the subclass to facilitate both scenarios.

在使用 之前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

What I'm trying to do is call this preparation method in my app delegate's didFinishLaunchingWithOptionsand wait for the completion block to be executed BEFORE returning either YESor NOat the end. My current approach doesn't work.

我想要做的是在我的应用程序委托中调用此准备方法didFinishLaunchingWithOptions并等待完成块在返回之前YESNO最后执行。我目前的方法不起作用。

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

How can I wait until the completion handler in prepareWithCompletionHandleris called before returning successful? I'm really confused.

我怎样才能等到完成处理程序在prepareWithCompletionHandler返回之前被调用successful?我真的很困惑。

回答by Rob

I'm unsure why the didFinishLaunchingreturn 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.

我不确定为什么didFinishLaunching返回状态取决于完成处理程序的成功,因为您显然甚至没有考虑launchOptions. 我不想看到你在这里放置一个同步调用(或者更准确地说,使用信号量将异步方法转换为同步方法),因为它会减慢应用程序的速度,如果它足够慢,你可能会被杀死看门狗过程。

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 prepareWithCompletionHandleris 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.

但是,在进一步prepareWithCompletionHandler正在做的事情后,它显然是在调用将自己的完成块分派到主队列的方法,因此任何使此同步的尝试都将死锁。

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

所以,使用异步模式。如果你想在 中启动这个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;
}

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

然后你可以让你的视图控制器addObserverForName观察这个通知。

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

或者,您可以将此代码移出应用程序委托并移至该视图控制器中,从而无需通知。

回答by Cy-4AH

For your case using dispatch group will be slightly different:

对于您使用 dispatch group 的情况会略有不同:

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

回答by David Berry

A lot of proposed solutions here using either dispatch_group_waitor semaphores, but the real solution is to rethink why you want to block from returning didFinishLaunchinguntil 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.

这里有很多建议的解决方案使用dispatch_group_wait信号量或信号量,但真正的解决方案是重新思考为什么要阻止返回,didFinishLaunching直到可能很长的异步请求完成。如果在操作完成之前你真的不能做任何有用的事情,我的建议是在初始化发生时显示某种加载请等待屏幕,然后立即从 didFinishLaunching 返回。