在app delegate中返回之前,等待异步任务完成完成块

Mat*_*ros 34 objective-c grand-central-dispatch ios

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

在使用之前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
Run Code Online (Sandbox Code Playgroud)

我想要做的是在我的app委托中调用这个准备方法,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;
}
Run Code Online (Sandbox Code Playgroud)

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

Rob*_*Rob 44

我不确定为什么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;
}
Run Code Online (Sandbox Code Playgroud)

但是,在进一步检查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;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以让视图控制器addObserverForName观察此通知.

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


Cy-*_*4AH 5

对于您的情况,使用调度组将略有不同:

- (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;
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*rry 5

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