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

2023-12-27

您能帮助我如何在以下内容时收到通知吗NSArrayController被修改,使用智能关键路径?

灵感来自

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

智能 KeyPaths:更好的 Swift 键值编码: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md 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 应用程序、Swift 4
  • 创建基于文档的应用程序
  • 使用核心数据

  • myArrayController的模式是实体名称,准备有Document.xcdatamodeld

  • myArrayController's 托管对象上下文一定会模型关键路径: representedObject.managedObjectContext
  • representedObject被分配了实例Document.
  • NSTableView's Content, 选择指标, and 排序描述符绑定到的对应关系myArrayController.

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

EDITED:

对于上面引用的例子,我改变了主意去观察managedObjectContext, 代替content of 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 of NSArrayController或实现两个功能

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

from NSTableViewDelegate. NSTableView's delegate连接到NSViewController.

有点令人惊讶的是,两者tableView()函数会被频繁调用。例如,在表中有十行的情况下,对行进行排序将得到十行didRemove呼叫后接十个didAdd来电;添加一行将导致十行didRemove打电话,然后十一点didAdd来电。那效率不太高。

对于修改,我们需要

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

from NSControlTextEditingDelegate, 一个超NSTableViewDelegate. Every NSTextField每个表列的应连接到NSViewController通过其delegate.

此外,不幸的是,这control()在文本编辑完成后立即调用,而是在实际值之前调用NSArrayController已经升级。也就是说,有点无用。我还没有找到第一种方法的良好解决方案。

ANYWAY,本文的主要主题是如何使用智能关键路径. :-)

EDITED 2:

我打算同时使用

  1. 观察属性content of NSArrayController... 第一个
  2. 观察一个Notification正在发布者NSManagedObjectContext... 第二个

1 是当用户更改主从视图时,这不会更改NSManagedObjectContext.

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

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

Thanks.

EDITED 3:

代码2.观察aNotification已完全更换为新的。

  • 如何注意到Core Data处理的托管对象实体的变化? https://stackoverflow.com/questions/44866967/how-to-notice-changes-on-managed-object-entity-handled-by-core-data

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

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)
        }
    }
}

The observe(...)函数返回一个瞬态观察者,其生命周期指示您将收到通知的时间长度。如果返回的观察者是deinitd,您将不再收到通知。就您而言,您从未保留该对象,因此它在方法作用域之后立即消失。

另外,要手动停止观察,只需设置mySub to nil,这隐含地deinit是旧的观察者对象。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Swift 4 中将键值观察与智能 KeyPath 结合使用? 的相关文章

随机推荐