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
继承的类型a
class

但是,在您给出的示例中,您并不真正需要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东西。一切都很好,只是编译器没有抱怨,我们有一个运行时崩溃(可能是在发货后)