我使用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
中的完成处理程序?我真的很困惑。
答案 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返回。