防止UIDocument openWithCompletionHandler在打开时被调用

时间:2013-02-24 12:43:47

标签: ios core-data objective-c-blocks uimanageddocument uidocument

我有一个单例类(DTTSingleton),其中包含以下方法:

+ (UIManagedDocument *)managedDocument
{    
    static UIManagedDocument *managedDocument = nil;
    static dispatch_once_t mngddoc;

    dispatch_once(&mngddoc, ^
    {
        if(!managedDocument)
        {
            NSURL *url = [[DTTHelper applicationDocumentsDirectory] URLByAppendingPathComponent:kDTTDatabaseName];
            managedDocument = [[DTTManagedDocument alloc] initWithFileURL:url];            
        }
    });

    return managedDocument;
}

+ (void)useDefaultDocumentWithBlock:(completion_block_t)completionBlock
{
    if (![[NSFileManager defaultManager] fileExistsAtPath:[DTTSingleton.managedDocument.fileURL path]])
    {
        [DTTSingleton.managedDocument saveToURL:DTTSingleton.managedDocument.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success)
         {
             if (success)
             {
                 completionBlock(DTTSingleton.managedDocument.managedObjectContext);                 
             }
             else
             {
                 NSLog(@"Failed to save!");
             }
         }];
    }
    else if (DTTSingleton.managedDocument.documentState == UIDocumentStateClosed)
    {
        [DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success)
         {
             if (success)
             {
                 completionBlock(DTTSingleton.managedDocument.managedObjectContext);
             }
             else
             {
                 NSLog(@"Failed to open!");
             }
         }];
    }
    else if (DTTSingleton.managedDocument.documentState == UIDocumentStateNormal)
    {
        completionBlock(DTTSingleton.managedDocument.managedObjectContext);
    }
}

在我的UITableViewController中,我在viewDidLoad方法中有以下代码:

   [DTTSingleton useDefaultDocumentWithBlock:^(NSManagedObjectContext *moc)
    {
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"SomeEntity"];
        self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc cacheName:nil];
    }];
    [DTTSingleton useDefaultDocumentWithBlock:^(NSManagedObjectContext *moc)
     {
         NSLog(@"When this is called it errors because DTTSingleton is already trying to open it!");
     }];

执行时我收到错误:

  

由于未捕获的异常而终止应用   'NSInternalInconsistencyException',原因:'尝试打开或者   还原已在航班中打开或还原操作的文档

我理解为什么我收到此错误,因为我正在尝试在另一个打开过程已经运行时打开文档。所以我的问题是......

1)我如何确保只调用openWithCompletionHandler一次?

2)如何在文档打开后确保执行第二个块?

感谢您的帮助!

2 个答案:

答案 0 :(得分:4)

我不确定你是否已经看过这个,但可能会有一个很好的资源:http://adevelopingstory.com/blog/2012/03/core-data-with-a-single-shared-uimanageddocument.html

如果您只是想创建自己的(而不是使用上面的链接 - 或类似的)并且正在寻找一些输入,我可以指出我看到的一些事情(虽然我没有声称任何手段成为专家)...

无论如何,我相信你的问题源于此:

// ....
} else if(DTTSingleton.managedDocument.documentState == UIDocumentStateClosed) {

    [DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success) {
         if(success) {
             completionBlock(DTTSingleton.managedDocument.managedObjectContext);
         } else {
             NSLog(@"Failed to open!");
         }
     }];

}

方法openWithCompletionHandler尝试异步打开与文档的连接。这个问题是,在您第一次打开UITableView中打开文档时,您正在使用的代码会注意到文档已关闭 - 因此它会尝试打开它。这一切都很好,但是你在这里使用的代码会重新发出另一个创建单例实例的尝试。更有可能的是,这种情况发生得如此之快(并且紧密结合在一起),它又一次尝试异步打开文档。

要测试这个,请尝试在UIDocumentStateClosed检查之后放置一个断点:

[DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success)

我相信你会看到这次被执行多次......

我没有足够的技巧来解释如何解决这个问题,但我会认真地推荐使用上面链接中显示的方法,他应用块来分配/跟踪打开文档的存在

希望有帮助吗?

编辑: *添加了断点的建议。

编辑:Here is another stackoverflow ticket with a similar issue (and suggestion/conclusion) - 所以我可能不会离这里太远=)

答案 1 :(得分:1)

刚想我回帖说我解决的问题是通过禁用访问Core Data的按钮直到文档准备就绪来解决的,这样你就永远不会尝试同时打开文档处理。至于生命周期处理程序(如viewDidLoad)中的核心数据访问,我实现了一个系统,如果文档打开(状态用变量手动保存),它会通过循环来延迟调用,直到文档打开,实质上是排队调用。不要忘记使用performSelector:withObject:afterDelay:在循环调用中循环,否则会导致应用程序崩溃。

感谢John的建议。