【问题标题】:Prevent UIDocument openWithCompletionHandler being called when already opening防止在已经打开时调用 UIDocument openWithCompletionHandler
【发布时间】:2013-02-24 12:43:46
【问题描述】:

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

感谢您的帮助!

【问题讨论】:

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


    【解决方案1】:

    我不确定你是否看过这个,但这里可能会有一个很好的资源:@​​987654321@

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

    无论如何,我相信您的问题源于这里:

    // ....
    } 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) - 毕竟我可能离这里不远=)

    【讨论】:

    • adevelopingstory 链接已失效。
    【解决方案2】:

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

    感谢约翰的建议。

    【讨论】:

      猜你喜欢
      • 2014-03-08
      • 2012-08-09
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      • 1970-01-01
      • 2018-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多