OS X Core Data - 将托管对象上下文传递给视图控制器

Posted

技术标签:

【中文标题】OS X Core Data - 将托管对象上下文传递给视图控制器【英文标题】:OS X Core Data - Passing a Managed Object Context to a View Controller 【发布时间】:2016-06-13 12:12:45 【问题描述】:

我正在 Xcode 7.3.1 中开发 Mac 应用程序。我正在尝试将模型对象上下文从我的 AppDelegate 传递给 ArrayController。

我有一个名为 DataController 的类,它创建了我的核心数据堆栈。 DataController.managedObjectContext 保存托管对象上下文。

我的 AppDelegate 类如下:

class AppDelegate: NSObject, NSApplicationDelegate 

  var dataController: DataController!

  func applicationDidFinishLaunching(aNotification: NSNotification) 
    // Insert code here to initialize your application

    // Create an instance of the DataController class.
    dataController = DataController()

    // Create a reference to the first ViewController embedded in the WindowController.
    guard let splitViewController = NSApplication.sharedApplication().windows[0].contentViewController as? ManagedObjectContextSettable
        else  fatalError("Wrong view controller type")

    // Set the managedObjectContext property.
    splitViewController.managedObjectContext = dataController.managedObjectContext
  

  func applicationWillTerminate(aNotification: NSNotification) 
    // Insert code here to tear down your application
  


在我的故事板中,我在 WindowController 中嵌入了一个 SplitViewController。 SplitViewController 有自己的自定义视图控制器类,名为 SplitViewController。这是SplitViewController中的代码:

class SplitViewController: NSSplitViewController, ManagedObjectContextSettable 

  var managedObjectContext: NSManagedObjectContext!

  override func viewDidLoad() 
    super.viewDidLoad()
    // Do view setup here.


    // Create a reference to the first ViewController embedded in the WindowController.
    let childControllers = self.childViewControllers

    print("childControllers.count = \(childControllers.count)")

    for childController in childControllers

        if childController.isKindOfClass(TableViewController)

            print("Found TableViewController")

            guard let tableViewController = childController as? ManagedObjectContextSettable
                else  fatalError("Wrong view controller type")

            tableViewController.managedObjectContext = managedObjectContext

        
    
  

其中一个拆分视图项是我的 TableView,它有自己的视图控制器,名为 TableViewController。下面是 TableViewController 的代码:

class TableViewController: NSViewController, ManagedObjectContextSettable, NSTableViewDataSource, NSTableViewDelegate 

  @IBOutlet weak var tableView: NSTableView!

  var managedObjectContext: NSManagedObjectContext!

  override func viewDidLoad() 
    super.viewDidLoad()
    // Do view setup here.
    //print(managedObjectContext.description)
  


在情节提要中,我拖动了一个 ArrayController,在 Inspector 的 Bindings 选项卡中,我设置了 Bind To 并选择了 TableViewController 并将 Model Key Path 设置为“self.managedObjectContext”。最终它没有收到托管对象上下文。

我无法确定是否应该为嵌入式 ViewController 重写 prepareForSegue 函数,我阅读的每个示例都是针对 ios 的。

请问我哪里出错了?

【问题讨论】:

【参考方案1】:

applicationDidFinishLaunching 可以在viewDidLoad 之后执行。当设置了 splitViewController 的managedObjectContext 时,设置了 childControllers 的managedObjectContext

绑定使用 KVO。将 var managedObjectContext 更改为 dynamic var managedObjectContext 以使属性符合 KVO。

【讨论】:

我注意到 AppDelegate.applicationDidFinishLoading 是在 ViewController 的所有 viewDidLoad 方法之后执行的。您是否建议我在 AppDelegate.applicationDidFinishLoading 方法中设置 SplitViewController 的子视图控制器(TableViewController)的 managedObjectContext 变量? 我建议viewDidLoadSplitViewController 可以在viewDidLoadTableViewController 之前执行。实现managedObjectContext的setter并传播到childControllers。 另一种可能的解决方案是以编程方式将数组控制器的managedObjectContext 绑定到dataController.managedObjectContext。 (我认为情节提要和视图控制器的开发人员从未听说过绑定)【参考方案2】:

如果您使用带有 Cocoa 绑定的数组控制器,您必须重写 init(coder:) 方法并在那里初始化托管上下文以执行隐式初始获取。 viewDidLoad 来不及了。

segue 工作流程与 iOS 中的相同。更方便,因为有一个属性presentingViewController 可以获取对父视图控制器的引用。

Core Data Manager DataController 应该是一个单例,以确保托管对象上下文实例始终相同。

【讨论】:

非常有帮助。那我是不是重写AppDelegate中的init将MOC传递给SplitViewController,然后重写SplitViewController中的prepareForSegue将MOC传递给TableViewController? 不在 AppDelegate 中,仅在使用数组控制器和绑定的控制器中。如果使用单例,则在 AppDelegate 中初始化堆栈,然后从单例中获取上下文。 我直接在 ArrayController 所在的控制器中创建了我的 DataController 类的实例。那么为什么 Apple 建议我将 MOC 从 AppDelegate 传递给 Window 的子视图控制器,并从它们向下传递给它们的子视图? Apple 不建议使用自定义核心数据控制器类的不同实例。您可以按照推荐的方式将核心数据代码保留在 AppDelegate 中并分别传递托管上下文,但是您必须执行额外的“手动”获取,因为绑定是在调用 viewDidLoad 之前建立的。 fetch: 被推迟到运行循环的下一次迭代。在applicationDidFinishLaunching 中设置managedObjectContext 已经足够早了。【参考方案3】:

这可能不是一个好的做法,但它似乎有效。

我将我的 DataController 类设为单例类,以确保只有一个托管对象上下文。在我的 TableViewController 中,我创建了一个 managedObjectContext 属性,如下所示:

    lazy var managedObjectContext = DataController.sharedInstance.managedObjectContext

在我的数组控制器中,我将托管对象上下文参数绑定到 TableViewController 并将模型键路径设置为 self.managedObjectContext。

可以改进吗?

【讨论】:

以上是关于OS X Core Data - 将托管对象上下文传递给视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

使用Core Data将文本从文本字段分配到托管对象上下文

在后台线程上安全保存 Core Data 托管对象上下文的正确方法?

防止托管对象被 Core Data 再次出错

Core Data 3 托管对象上下文

如何使用 Core Data 设置 NSArrayController?

如果您想通过 Core Data 使用多个实体,您需要为每个实体提供一个托管对象上下文吗?