首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Swift 4中使用智能KeyPaths进行键值观察?

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

Stack Overflow用户
提问于 2017-06-29 14:13:42
回答 1查看 8.5K关注 0票数 8

NSArrayController的内容被修改时,您能帮助我如何使用智能KeyPaths获得通知吗?

灵感来自

键值观察ref/doc/uid/TP40014216-CH7-ID12

智能KeyPaths: Swifthttps://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md的更好的键值编码

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

代码语言:javascript
复制
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)")
    }
  }
}

然而,这是行不通的。对目标对象所做的任何更改都不会触发通知。

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

代码语言:javascript
复制
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,可可应用,Swift 4
  • 创建基于文档的应用程序
  • 使用核心数据
  • myArrayController的模式是实体名,由Document.xcdatamodeld编写。
  • myArrayController托管对象上下文绑定到模型密钥路径representedObject.managedObjectContext
  • representedObjectDocument实例一起分配。
  • NSTableViewContent选择索引排序描述符绑定到myArrayController的对应项。

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

编辑的

关于上面引用的例子,我改变了主意,以观察managedObjectContext,而不是content of NSArrayController

代码语言:javascript
复制
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 of NSArrayController或实现两个函数

代码语言:javascript
复制
func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int)
func tableView(_ tableView: NSTableView, didRemove rowView: NSTableRowView, forRow row: Int)

来自NSTableViewDelegateNSTableViewdelegateNSViewController相连。

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

对于修改,我们需要

代码语言:javascript
复制
func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool

来自NSControlTextEditingDelegateNSTableViewDelegate的超级品牌。每个表列的每个NSTextField都应该通过其NSViewControllerdelegate连接到NSViewController

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

无论如何,,这篇文章的主要主题是如何使用智能KeyPaths。:-)

编辑2

我要用这两种

  1. 观察content of NSArrayController .第一个
  2. 观察NotificationNSManagedObjectContext .第二个

1用于当用户更改主细节视图时,该视图不会在NSManagedObjectContext上进行更改。

2用于当用户对其进行更改时:添加、删除、更新以及撤销、命令-Z,它不伴随鼠标事件。

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

谢谢。

编辑3

观察Notification的代码2已经完全被新的代码所取代。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-07-14 08:19:30

至于您的初始代码,如下所示:

代码语言:javascript
复制
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是旧的观察者对象。

票数 21
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44827613

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档