繁体   English   中英

等待异步任务完成完成块,然后返回应用程序委托

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

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

我想做的是在我的应用程序委托的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;
}

在返回successful之前,我如何等待直到prepareWithCompletionHandler的完成处理程序被调用? 我真的很困惑

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM