Swift 4(BETA 2)KVO崩溃,基于WWDC谈话 我试图得到与WKDC 2017基金会的KVO观测工作类似的例子。我看到的唯一不同之处是,我必须调用super.init(),并且必须隐式地打开“kvo”标记
以下内容用于操场:Swift 4(BETA 2)KVO崩溃,基于WWDC谈话 我试图得到与WKDC 2017基金会的KVO观测工作类似的例子。我看到的唯一不同之处是,我必须调用super.init(),并且必须隐式地打开“kvo”标记,swift,key-value-observing,Swift,Key Value Observing,以下内容用于操场: struct Node { let title: String let leaf: Bool var children: [String: Node] = [:] } let t = Node(title:"hello", leaf:false, children:[:]) let k1 = \Node.leaf let k2 = \Node.children t[keyPath: k1] // returns "false" works t[ke
struct Node {
let title: String
let leaf: Bool
var children: [String: Node] = [:]
}
let t = Node(title:"hello", leaf:false, children:[:])
let k1 = \Node.leaf
let k2 = \Node.children
t[keyPath: k1] // returns "false" works
t[keyPath: k2] // returns "[:]" works
@objcMembers class MyController : NSObject {
dynamic var tr: Node
var kvo : NSKeyValueObservation!
init(t: Node) {
tr = t
super.init()
kvo = observe(\.tr) { object, change in
print("\(object) \(change)")
}
}
}
let x = MyController(t: t)
x.tr = Node(title:"f", leaf:false, children:[:])
x
此错误:
致命错误:无法从KeyPath提取字符串
Swift.ReferenceWritableKeyPath:file/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.45.6/src/Swift/stdlib/public/SDK/Foundation/NSObject.Swift,
第85行
另外,请参见此错误:
错误:执行被中断,原因:EXC\u错误\u指令
(代码=EXC_I386_INVOP,子代码=0x0)。这一过程被留在了最后阶段
在中断的地方,使用“threadreturn-x”返回到
表达式计算之前的状态
是否有其他人能够实现类似的功能,或者这是我需要报告的错误?这里的错误是编译器允许您说:
@objcMembers class MyController : NSObject {
dynamic var tr: Node
// ...
节点
是一个结构
,因此不能直接在Obj-C中表示。但是,编译器仍然允许您将tr
标记为动态
——这需要@objc
。虽然@objcMembers
为类的成员推断@objc
,但它仅对可直接在Obj-C中表示的成员进行推断,而tr
不是
所以实际上,编译器不应该让您将tr
标记为dynamic
——我继续了,这将为Swift 5做好准备
tr
必须是@objc
&动态的
,您才能在其上使用KVO,因为KVO需要方法swizzling,而Obj-C运行时提供,而Swift运行时不需要。因此,要在此处使用KVO,您需要将节点
设为类
,并从NSObject
继承,以便将tr
公开给Obj-C:
class Node : NSObject {
let title: String
let leaf: Bool
var children: [String: Node] = [:]
init(title: String, leaf: Bool, children: [String: Node]) {
self.title = title
self.leaf = leaf
self.children = children
}
}
(如果您再次查看WWDC视频,您将看到他们观察到的属性实际上是从NSObject
继承的类型aclass
)
但是,在您给出的示例中,您并不真正需要KVO–您可以将节点
保留为结构
,而使用属性观察者:
struct Node {
let title: String
let leaf: Bool
var children: [String: Node] = [:]
}
class MyController : NSObject {
var tr: Node {
didSet {
print("didChange: \(tr)")
}
}
init(t: Node) {
tr = t
}
}
let x = MyController(t: Node(title:"hello", leaf:false, children: [:]))
x.tr = Node(title:"f", leaf: false, children: [:])
// didChange: Node(title: "f", leaf: false, children: [:])
由于Node
是一种值类型,didSet
也会触发对其属性的任何更改:
x.tr.children["foo"] = Node(title: "bar", leaf: false, children: [:])
// didChange: Node(title: "f", leaf: false, children: [
// "foo": kvc_in_playground.Node(title: "bar", leaf: false, children: [:])
// ])
据苹果称,这是目前的预期行为,因为它取决于Objective-C运行时。这是他们对我的错误报告的回应,它进一步证实了被接受的回答海报所说的 公认的答案是正确的。但我想在Swift中谈谈我对KVO的了解
Swift
还可以在许多工具包和实现中混合编译OC
,例如KVO。因此,您应该知道KVO是如何在OC
中实现的
当您为对象添加Observer:forKeyPath:时,运行时会创建一个子类继承对象所属的类,然后为对象重写setter
方法,当对象更改时,它会调用setter
和setValue(value:Any?,forKey:String)
通知更改
现在,让我们回到Swift,因此您的keyPath
应该是OC
接受类型
class A { // it's a Swift class but not a OC class inherit from NSObject
var observation: NSKeyValueObservation?
@objc dynamic var count: Int = 0 // @objc for OC, dynamic for setter
}
observation = observe(\.count, options: [.new, .old]) { (vc, change) in
print("new: \(change.newValue), old: \(change.oldValue)")
} // it's very strange when don't use result, the observe is failure.
以上是我对KVO的了解,我将不断搜索并更新我的答案。谢谢。我确实知道didSet,但我正在尝试新的KVO东西。一切都很好,只是编译器没有抱怨,我们有一个运行时崩溃(可能是在发货后)