如何在 Swift 4 中将键值观察与智能键路径结合使用?



你能帮我如何使用智能键路径管理在修改NSArrayController内容时收到通知吗?

灵感来源

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

Smart KeyPaths:更好的 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的内容、选择索引排序描述符绑定到myArrayController的对应关系中。

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

已编辑

关于上面引用的示例案例,我改变了主意,以观察managedObjectContext,而不是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)")
  }
}

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

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

对于添加和删除,我们需要观察NSArrayControllercontent或实现两个函数

func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int)
func tableView(_ tableView: NSTableView, didRemove rowView: NSTableRowView, forRow row: Int)

NSTableViewDelegate. NSTableViewdelegate连接到NSViewController

有点令人惊讶的是,这两个tableView()函数都会被如此频繁地调用。例如,在一个表中有十行的情况下,对行进行排序将导致十个didRemove调用,然后是十个didAdd调用;添加一行将导致 10 个didRemove调用,然后是 11 个didAdd调用。这效率不高。

对于修改,我们需要

func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool

NSControlTextEditingDelegate,一个超级NSTableViewDelegate。每个表列的每个NSTextField都应通过其delegate连接到NSViewController

此外,不幸的是,此control()是在文本版本完成后立即调用的,而是在NSArrayController中的实际值更新之前调用的。也就是说,在某种程度上,毫无用处。我还没有找到第一种方法的好解决方案。

无论如何,这篇文章的主要主题是如何使用智能键路径

编辑2

我将同时使用两者

  1. 观察NSArrayController的属性content...第一个
  2. 观察NSManagedObjectContext发布的Notification...第二个

1 表示用户更改大纲-详细信息视图时,该视图不会对NSManagedObjectContext进行更改。

2 表示用户对其进行更改时:添加、删除、更新和撤消 Command-Z,这不附带鼠标事件。

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

谢谢。

编辑3

代码 2. 观察Notification已完全替换为新代码。

  • 如何注意核心数据处理的托管对象实体的更改?

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

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 'd,您将不再收到通知。在您的情况下,您从未保留过该对象,因此它在方法作用域之后立即死亡。

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

相关内容

最新更新