如何在Swift 4中使用智能KeyPath进行键值观察?

时间:2017-06-29 14:13:43

标签: swift key-value-observing swift4

您能否帮我了解如何使用智能密钥路径修改NSArrayController的内容后收到通知?

受启发

键值观察https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12

智能密钥路径:更好的Swift键值编码https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

我模仿了文章的示例代码。

class myArrayController: NSArrayController {
  required init?(coder: NSCoder) {
    super.init(coder: coder)

    observe(\.content, options: [.new]) { object, change in
      print("Observed a change to \(object.content.debugDescription)")
    }
  }
}

但是,这不起作用。对目标对象所做的任何更改都不会触发通知。

相反,下面列出的典型方法是工作。

class myArrayController: NSArrayController {
  required init?(coder: NSCoder) {
    super.init(coder: coder)

    addObserver(self, forKeyPath: "content", options: .new, context: nil)
  }

  override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "content" {
      print("Observed a change to \((object as! myArrayController).content.debugDescription)")
    }
    else {
      super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    }
  }
}

新方式看起来更优雅。你有什么建议吗?

环境:Xcode 9 Beta

  • macOS,Cocoa App,Swift 4
  • 创建基于文档的应用程序
  • 使用核心数据

  • myArrayController模式为实体名称,使用Document.xcdatamodeld

  • 准备
  • myArrayController 托管对象上下文绑定到模型密钥路径representedObject.managedObjectContext
  • representedObject分配了Document
  • 的实例
  • NSTableView 内容选择索引排序描述符绑定到{的对应关系{1}}。

有关环境的更多信息: 绑定managedObjectContext,Xcode 8.3.2,Storyboards,mac https://forums.bignerdranch.com/t/binding-managedobjectcontext-xcode-8-3-2-storyboards-macos-swift/12284

EDITED

关于上面引用的示例案例,我改变主意观察myArrayController,而不是managedObjectContext的{​​{1}}。

content

原因是第二种方法比第一种方法简单。此代码涵盖了所有需要的要求:表格行的添加和删除,以及表格单元格的修改。值。缺点是每个表的修改和每个其他实体的修改。应用程序内的修改将导致通知。但是,这样的通知并不有趣。但是,这不是什么大问题。

相比之下,第一种方法需要更多的复杂性。

对于添加和删除,我们需要观察NSArrayController class myViewController: NSViewController { override func viewWillAppear() { super.viewWillAppear() let n = NotificationCenter.default n.addObserver(self, selector: #selector(mocDidChange(notification:)), name: NSNotification.Name.NSManagedObjectContextObjectsDidChange, object: (representedObject as! Document).managedObjectContext) } } @objc func mocDidChange(notification n: Notification) { print("\nmocDidChange():\n\(n)") } } 或实现两个函数

content

来自NSArrayControllerfunc tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int) func tableView(_ tableView: NSTableView, didRemove rowView: NSTableRowView, forRow row: Int) NSTableViewDelegate已与NSTableView相关联。

稍微令人惊讶的是,两个delegate函数将被频繁调用。例如,在表格中有十行的情况下,排序行将导致十次NSViewController次呼叫,然后是十次tableView()次呼叫;添加一行将导致十次didRemove次呼叫,然后进行十一次didAdd次呼叫。那不是那么有效。

如需修改,我们需要

didRemove

来自didAdd,超级func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool 。每个表格列的每个NSControlTextEditingDelegate都应通过其NSTableViewDelegateNSTextField相关联。

此外,遗憾的是,在文本编辑完成后立即调用此NSViewController,而在delegate中的实际值更新之前调用此control()。那是有点无用的。我还没有找到第一种方法的好解决方案。

无论如何,这篇文章的主要内容是如何使用智能密钥路径。 : - )

编辑2

我将同时使用

  1. 观察NSArrayController的属性content ...第一个
  2. 观察由NSArrayController张贴的Notification ...第二个
  3. 1表示当用户更改主要详细信息视图时,该视图不会对NSManagedObjectContext进行更改。

    2表示用户对其进行更改:添加,删除,更新以及撤消 Command-Z ,但不包含鼠标事件。

    目前,将使用NSManagedObjectContext的版本。一旦解决了这篇文章的问题,我将切换到addObserver(self, forKeyPath: "content", ...

    的版本

    感谢。

    已编辑3

    代码2.观察observe(\.content, ...已被完全替换为新代码。

1 个答案:

答案 0 :(得分:16)

至于你的初始代码,这里应该是这样的:

class myArrayController: NSArrayController {
    private var mySub: Any? = nil

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        self.mySub = self.observe(\.content, options: [.new]) { object, change in
            debugPrint("Observed a change to", object.content)
        }
    }
}

observe(...)函数返回一个瞬态观察者,其生命周期表示您将收到通知的时间。如果返回的观察者为deinit,则您将不再收到通知。在您的情况下,您从未保留该对象,因此它在方法范围之后就死了。

此外,要手动停止观察,只需将mySub设置为nil,隐式deinit是旧的观察者对象。