在 Storyboard 场景中使托管对象上下文可用于数组控制器

Posted

技术标签:

【中文标题】在 Storyboard 场景中使托管对象上下文可用于数组控制器【英文标题】:Making Managed Object Context available to array controller in Storyboard scene 【发布时间】:2016-03-14 00:16:06 【问题描述】:

我有一个核心数据、基于文档的故事板 OS X 应用程序。

这里有一个测试项目:

https://github.com/ericgorr/sb_ac_doc

这只是 Xcode 创建的默认项目,稍加修改。

我有一个单一的视图控制器,该视图包含一个表格视图,它最终将显示核心数据文档中的数据。我还将使用数组控制器来管理表格视图的行。

三个场景分别是:

应用场景 窗口控制器场景 主视图控制器场景

主视图控制器场景包含 TestItem 数组控制器

TestItem 视图控制器包含代码:

class ViewController: NSViewController

    var context: NSManagedObjectContext?

    //
    // ...
    //    

TestItem 数组控制器包含参数绑定:

Managed Object Context
    Bind to: Main View Controller
    Model Key Path: context

在 Document.swift 中:

override func makeWindowControllers()

    let moc = self.managedObjectContext!

    // Returns the Storyboard that contains your Document window.
    let storyboard          = NSStoryboard(name: "Main", bundle: nil)
    let windowController    = storyboard.instantiateControllerWithIdentifier( "Document Window Controller" ) as! NSWindowController
    let contentController   = windowController.contentViewController as! ViewController

    NSLog( "%@", self.managedObjectContext! )

    contentController.context = self.managedObjectContext!

    NSLog( "%@", contentController.context! )

    self.addWindowController( windowController )

这是我在阵列控制器尝试访问之前将 MOC 传递给视图控制器的尝试。

NSLog 都打印出有效值。

但是,我遇到了以下崩溃:

2016-03-13 20:13:02.667 sb_ac_doc[73021:16415257] Cannot perform operation without a managed object context
2016-03-13 20:13:02.669 sb_ac_doc[73021:16415257] (
    0   CoreFoundation                      0x00007fff956f9ae2 __exceptionPreprocess + 178
    1   libobjc.A.dylib                     0x00007fff9a09073c objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff956f998d +[NSException raise:format:] + 205
    3   AppKit                              0x00007fff8c8224e7 -[_NSManagedProxy _managedObjectContext] + 66
    4   AppKit                              0x00007fff8c822507 -[_NSManagedProxy _persistentStoreCoordinator] + 22
    5   AppKit                              0x00007fff8c82257a -[_NSManagedProxy _entity] + 46
    6   AppKit                              0x00007fff8c822802 -[_NSManagedProxy fetchRequestWithSortDescriptors:limit:] + 89
    7   AppKit                              0x00007fff8c821fec -[NSObjectController(NSManagedController) _executeFetch:didCommitSuccessfully:actionSender:] + 75
    8   AppKit                              0x00007fff8c5c18c2 -[NSController _controllerEditor:didCommit:contextInfo:] + 185
    9   CoreFoundation                      0x00007fff955c417c __invoking___ + 140
    10  CoreFoundation                      0x00007fff955c3fce -[NSInvocation invoke] + 286
    11  CoreFoundation                      0x00007fff95663be6 -[NSInvocation invokeWithTarget:] + 54
    12  Foundation                          0x00007fff87cce345 __NSFireDelayedPerform + 377
    13  CoreFoundation                      0x00007fff9563fbc4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
    14  CoreFoundation                      0x00007fff9563f853 __CFRunLoopDoTimer + 1075
    15  CoreFoundation                      0x00007fff956bde6a __CFRunLoopDoTimers + 298
    16  CoreFoundation                      0x00007fff955facd1 __CFRunLoopRun + 1841
    17  CoreFoundation                      0x00007fff955fa338 CFRunLoopRunSpecific + 296
    18  HIToolbox                           0x00007fff8add1935 RunCurrentEventLoopInMode + 235
    19  HIToolbox                           0x00007fff8add1677 ReceiveNextEventCommon + 184
    20  HIToolbox                           0x00007fff8add15af _BlockUntilNextEventMatchingListInModeWithFilter + 71
    21  AppKit                              0x00007fff8c25e0ee _DPSNextEvent + 1067
    22  AppKit                              0x00007fff8c62a943 -[NSApplication _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 454
    23  AppKit                              0x00007fff8c253fc8 -[NSApplication run] + 682
    24  AppKit                              0x00007fff8c1d6520 NSApplicationMain + 1176
    25  sb_ac_doc                           0x0000000100002427 main + 87
    26  libdyld.dylib                       0x00007fff987455ad start + 1
)

我应该使用什么正确模式以便阵列控制器可以访问文档的 MOC?

【问题讨论】:

【参考方案1】:

如果您使用绑定,则必须以符合 KVO 的方式修改属性。把dynamic放在var context: NSManagedObjectContext?前面。

【讨论】:

【参考方案2】:

我认为 ViewController 的属性context 丢失了。假设我们有一个核心数据堆栈。通常在新建项目时勾选Use Core Data选项即可自动生成。

然后实现 AppDelegate 中为 ViewController 的 context 创建的 managedObjectContext

var context: NSManagedObjectContext? 
    guard let appDelegate = NSApplication.sharedApplication().delegate as? AppDelegate else 
        return nil
    
    return appDelegate.managedObjectContext

至于其他小的修改,我通过 GitHub 创建了一个拉取请求。请作为参考。

【讨论】:

我不确定这是否适用于具有多个文档和多个 MOC 的应用程序。为什么在导致崩溃失败的代码运行之前不将 MOC 从 makeWindowControllers 中的 Document 传递给 ViewController? @ericgorr 我认为这是关于决策的。如果您希望将 MOC 从 makeWindowControllers 中的 Document 传递到 ViewController,则应在 IB 中取消选中 Prepare content,因为在 MOC 准备好之前加载情节提要。如果您更喜欢保留“准备内容”,您可以从 RAYWENDERLICH 找到一个教程:raywenderlich.com/21752/… 并检查完整的项目。它确实在 AppDelegate 类中准备了一个 MOC。 AppDelegate 没有 MOC。每个文档都有自己的 MOC。

以上是关于在 Storyboard 场景中使托管对象上下文可用于数组控制器的主要内容,如果未能解决你的问题,请参考以下文章

当一个托管对象在 moc A 中被删除时,它还会出现在 moc B 中吗?

如何在 python 3.5 中使对象可等待?

在托管对象上下文之间传递对象

核心数据:我的托管对象上的托管对象上下文为零

批量更新后更新托管对象上下文中的托管对象

核心数据获取...为啥在将托管对象插入上下文 A 并保存上下文 A 后,不使用上下文 B 获取托管对象?