1

我有一个带有以下方法的单例类(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)如何确保文档打开后执行第二个块?

谢谢你的帮助!

4

2 回答 2

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)

我相信你会看到这被执行了无数次......

我不够熟练,无法解释如何解决这个问题,但我会认真建议使用上面链接中显示的方法,他在其中应用块来分配/跟踪打开文档的存在

希望这有帮助吗?

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

编辑: 这是另一个具有类似问题(和建议/结论)的stackoverflow票- 所以我可能离这里不太远=)

于 2013-03-12T03:48:26.660 回答
1

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

感谢您的建议约翰。

于 2013-04-17T11:25:50.030 回答