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

时间:2014-04-07 14:04:33

标签: ios objective-c grand-central-dispatch

我使用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

我尝试做的是在我的app委托didFinishLaunchingWithOptions中调用此准备方法,并在返回YES或{{{}之前等待执行完成块1}}在最后。我目前的做法并不奏效。

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中的完成处理程序?我真的很困惑。

3 个答案:

答案 0 :(得分:43)

我不确定为什么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观察此通知。

或者,您可以将此代码移出应用代理并进入该视图控制器,从而无需通知。

答案 1 :(得分: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;
}

答案 2 :(得分:4)

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