通过 UIManagedDocument 异步打开时如何确保 NSManagedObjectContext

Posted

技术标签:

【中文标题】通过 UIManagedDocument 异步打开时如何确保 NSManagedObjectContext【英文标题】:How to ensure NSManagedObjectContext when opened asynchronously through UIManagedDocument 【发布时间】:2012-10-14 12:31:07 【问题描述】:

我有一个具有不同控制器的应用程序,它们都在同一个 NSManagedObjectContext 上运行。

我的方法是在我的 AppDelegate 中初始化 NSManagedObjectContext 并将其注入到所有控制器中。

我正在通过打开这样的 UIManagedDocument 来初始化我的 NSManagedObjectContext:

UIManagedDocument* databaseDoc = [[UIManagedDocument alloc] initWithFileURL:url];

if (![[NSFileManager defaultManager] fileExistsAtPath:[databaseDoc.fileURL path]]) 
    [databaseDoc saveToURL:databaseDoc.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) 
        myController.managedObjectContext = databaseDoc.managedObjectContext;
    ];
 else if (databaseDoc.documentState == UIDocumentStateClosed) 
    [databaseDoc openWithCompletionHandler:^(BOOL success) 
        myController.managedObjectContext = databaseDoc.managedObjectContext;
    ];
 else if (databaseDoc.documentState == UIDocumentStateNormal)
    myController.managedObjectContext = databaseDoc.managedObjectContext;

现在我的问题是,打开 UIManagedDocument 是异步发生的,并且 NSManagedObjectContext 仅在完成块中可用。

如何确保控制器始终有一个有效的 NSManagedObjectContext 可以使用?当然问题发生在启动时,即当控制器想要在他的“viewDidLoad”方法中使用 NSManagedObjectContext 时,AppDelegate 中的完成块尚未运行......

一种方法可能是在 AppDelegate 中“等待”直到 UIDocument 打开,但据我所知 this is not recommended ...

我想避免使用处理打开 NSManagedObjectContext 的异步性质的代码“污染”我的控制器...但也许这是一个天真的愿望?

【问题讨论】:

您应该将您的 uiviewcontroller managedObjectContext 实现为自定义设置方法 (setManagedObjectContext:(context*))。因此,即使在调用 viewDidload 方法时上下文不存在,您也可以在调用自定义设置器时设置上下文后创建您的 GUI 逻辑方法。也将其调用到您的 viewDidLoad 方法中。这样您就不会被绑定到同步的东西。 @Mr Bonjour 感谢您的提示。这就是我目前正在做的事情,但这基本上会导致“污染”我的视图控制器:例如,当我有一个创建新 NSManageObject 的按钮时,我必须先禁用它,然后在设置 NSManagedObjectContext 时启用它.. . 我想避免这种情况,因为 viewcontroller only 有理由存在,如果它可以依赖它的 NSManagedContext ... 那么你应该确保只有当你的 NSManagedContext 可用时才会调用你的 viewController。您可以在应用程序启动时设置 displayController 来执行此操作,然后当您的上下文可用时,您可以调用另一个 viewController。请注意,您几乎无法同时获得 GUI 和上下文,因为它们都是异步获取数据的 @Mr Bonjour:谢谢。在应用启动时设置 displayContoller 的建议听起来很有趣。您能否在正确的答案中详细说明(然后我可以接受作为解决方案)。我会对如何实现这一点感兴趣......这将是初始控制器,然后在初始化后以编程方式连接到下一个应用程序控制器吗?如何在这个“初始化控制器”中等待? 【参考方案1】:

在您的 appDelegate 中:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

    MyWaitViewController* waitController = [[MyWaitViewController new] autorelease];
    self.window.rootViewController =  waitController;

// then somewheres else, when you get your context
  [databaseDoc saveToURL:databaseDoc.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) 
        myContextController.managedObjectContext = databaseDoc.managedObjectContext;
        self.window.rootViewController    = myContextController;
        // note that at this point when the viewDidLoad method will get called
        // it will have his managedObjectContext and his view already available.
        // you can change your rootController, or push another viewController into the
 // stack. Depending on what u want from the GUI side

    ];
    return YES;

请注意,您将 GUI 逻辑部署到 MyWaitViewController + AppDelegate 端。但是你让你的“myContextController”远离那个逻辑控制,因为他只有在上下文存在时才会被调用/创建。

【讨论】:

我想这就是我想要的。但是我正在使用故事板。我将不得不调查您以编程方式设置 rootViewController 的方法如何与使用故事板兼容...【参考方案2】:

我在同样的问题上苦苦挣扎,我通过使用NSNotificationCenter 来解决这个问题。

在成功处理程序中初始化NSManagedObjectContext 时,添加发送通知。 然后,将侦听器添加到您的第一个ViewControllerviewDidLoad

我使用该侦听器调用reloadData 方法。在一个重型应用程序中,这可能是一个问题,因为viewcontroller 加载空白,然后重新加载数据,但这是一个精简版,而且很明显 - viewControllermanagedObjectContext 立即加载。

【讨论】:

以上是关于通过 UIManagedDocument 异步打开时如何确保 NSManagedObjectContext的主要内容,如果未能解决你的问题,请参考以下文章

如何覆盖 UIManagedDocument 中的 NSPersistentStoreCoordinator

如何使用块为我的整个应用程序共享的每个磁盘上的文档创建一个全局 UIManagedDocument 实例?

在核心数据中使用 UIManagedDocument 的标准(或正确)方法是啥

无法创建 UIManagedDocument

UIManagedDocument 的迁移问题

UIManagedDocument 和 NSFetchedResultsController