在应用程序委托中返回之前,等待asynchronous任务完成完成块

我正在使用UIManagedDocument的子类在我的项目中使用核心数据。 重点是子类返回一个单例实例,以便我的屏幕可以简单地调用它,并且托pipe对象上下文对于所有这些实例都保持相同。

在使用UIManagedDocument之前,我需要打开它,如果它的文件path已经存在,或者创build它,如果它尚未。 我创build了一个方便的方法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 。 我不希望看到你在这里放置一个同步调用(或者更准确地说,使用一个信号量来将一个asynchronous方法转换为一个同步方法),因为它会减慢应用程序的速度,如果速度太慢,看门狗进程。

信号量是使asynchronous进程同步的一种常用技术:

 - (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的工作后,显然是调用方法将自己的完成块分派给主队列,所以任何使这个同步的尝试都会死锁。

所以,使用asynchronous模式。 如果你想在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观察这个通知。

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

这里提出了很多使用dispatch_group_wait或信号量的解决scheme,但真正的解决scheme是重新思考为什么要阻止从didFinishLaunching返回,直到可能很长的asynchronous请求完成。 如果在操作完成之前确实无法做任何其他操作,我的build议是在初始化发生时显示某种加载请等待屏幕,然后立即从didFinishLaunching返回。

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

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