46

我一直在试图找到一个例子,但我所看到的在我的情况下不起作用。

什么相当于以下代码:

object.addObserver(self, forKeyPath: "keyPath", options: [.new], context: nil)

override public func observeValue(
    forKeyPath keyPath: String?,
    of object: Any?, 
    change: [NSKeyValueChangeKey : Any]?, 
    context: UnsafeMutableRawPointer?) {

}

上面的代码有效,但我收到了来自 SwiftLink 的警告:

使用 Swift 3.2 或更高版本时,更喜欢新的基于块的 KVO API 和 keypaths。

如果您能指出我正确的方向,我将不胜感激。

4

2 回答 2

103

Swift 4 引入了一系列具体的Key-Path 类型,一个新的Key-Path Expression来生成它们,以及一个新的基于闭包的观察函数,可用于继承的类NSObject

使用这组新功能,您的特定示例现在可以更简洁地表达:

self.observation = object.observe(\.keyPath) { 
    [unowned self] object, change in
    self.someFunction()
}

涉及的类型

键路径语法

Key-Path Expression的一般语法遵循具体类型名称(包括任何通用参数)和一个或多个属性、下标或可选链接/强制展开后缀的链的形式。此外,如果 keyPath 的 Type 可以从 context 中推断出来,则可以省略,从而得到最简洁的.\Type.keyPathTypekeyPath\.keyPath

这些都是有效的键路径表达式:

\SomeStruct.someValue
\.someClassProperty
\.someInstance.someInnerProperty
\[Int].[1]
\[String].first?.count
\[SomeHashable: [Int]].["aStringLiteral, literally"]!.count.bitWidth

所有权

您是函数返回的NSKeyValueObservation实例的所有者,这意味着您不必也不必再这样做;相反,只要您需要观察观察,您就会一直强烈地参考它。observe addObserverremoveObserver

您也不需要这样做invalidate():它会很deinit优雅。因此,您可以让它一直存在,直到持有它的实例死亡,通过nil引用手动停止它,或者甚至invalidate()在您出于某种臭名昭著的原因需要保持实例存活时调用。

注意事项

您可能已经注意到,观察仍然潜伏在 Cocoa 的 KVO 机制的范围内,因此它仅适用于 Obj-C 类和 Swift 类继承NSObject(每个 Swift-dev 最喜欢的类型),并附加要求您打算观察的任何值,必须标记为@objc(每个 Swift 开发者最喜欢的属性)并声明为dynamic.

话虽这么说,整体机制是一个值得欢迎的改进,特别是因为它设法 Swiftify 观察NSObjects从我们可能碰巧需要使用的模块(例如Foundation)导入的,并且不会冒削弱我们努力获得的表达能力的风险。击键。

作为旁注,仍然需要Key-Path字符串表达式动态访问 NSObjectKVC 的属性或调用value(forKey(Path):)

超越 KVO

Key-Path Expressions 的功能远不止 KVO。\Type.path表达式可以存储为KeyPath对象以供以后重用。它们有可写、部分和类型擦除的味道。它们可以增强为合成而设计的 getter/setter 函数的表达能力,更不用说它们在让那些最有胆量的人深入研究 Lenses 和 Prisms 等功能概念世界中所起的作用。我建议您查看下面的链接,以了解更多关于他们可以打开的许多开发门。

链接:

键路径表达式@ docs.swift.org

KVO 文档@Apple

Swift Evolution Smart KeyPaths 提案

Ole Begemann 的 Whats-new-in-Swift-4 操场与 Key-Path 示例

WWDC 2017 视频:Foundation 中的新增 功能 SKP 4:35 和 KVO 19:40。

于 2017-11-30T21:16:04.240 回答
10

当我在 iOS 10 中使用此方法时,我的应用程序崩溃时添加一些内容。

在 iOS 10 中,您仍然需要在解除分配类之前删除观察者,否则您将遇到崩溃NSInternalInconsistencyException,说明:

A一个Class的实例C被释放,而键值观察者仍向其注册。

为了避免这种崩溃。只需将您正在使用的观察者属性设置为nil.

deinit {
    self.observation = nil
}    
于 2018-08-10T12:52:13.690 回答