Swift 4(BETA 2)KVO崩溃,基于WWDC谈话

pos*_*sen 13 key-value-observing swift

我试图得到一些非常类似于WWV 2017基金会谈论KVO观察的例子.我看到的唯一不同于那次谈话的差异是,我不得不调用super.init(),我不得不隐藏地解开"kvo"令牌.

以下用于游乐场:

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
Run Code Online (Sandbox Code Playgroud)

这个错误:

致命错误:无法从KeyPath中提取字符串Swift.ReferenceWritableKeyPath <__ lldb_expr_3.MyController,__ lldb_expr_3.Node>:file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.45.6/src/swift/stdlib /public/SDK/Foundation/NSObject.swift,第85行

另外,请看到此错误:

错误:执行被中断,原因:EXC_BAD_INSTRUCTION(代码= EXC_I386_INVOP,子代码= 0x0).该过程一直处于被中断的位置,使用"thread return -x"返回表达式求值之前的状态.

是否有其他人能够得到这样的工作,或者这是我需要报告的错误?

Ham*_*ish 21

这里的错误是编译器让你说:

@objcMembers class MyController : NSObject {
    dynamic var tr: Node
    // ...
Run Code Online (Sandbox Code Playgroud)

Node是一个struct,所以不能直接用Obj-C表示.但是,编译器仍允许您标记trdynamic- 这需要 @objc.虽然@objcMembers推断@objc了类的成员,但它只对可直接在Obj-C中表示的成员这样做,而tr不是.

所以真的,编译器不应该让你标记trdynamic- 我继续在这里提交了一个bug,现在已经修复了,并且将为Swift 5做好准备.

tr 需要@objcdynamic你使用就可以了志愿,因为志愿要求的方法混写,其中对象- C运行时提供,和斯威夫特运行时不.所以要在这里使用KVO,你需要创建Node一个class,并继承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
    }
}
Run Code Online (Sandbox Code Playgroud)

(如果你再次看一下WWDC视频,你会看到他们观察到的属性实际上class是继承的类型a NSObject)

但是,在你给出的例子中,你并不真正需要KVO - 你可以保持Node为a struct,而是使用属性观察者:

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: [:])
Run Code Online (Sandbox Code Playgroud)

因为它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: [:])
// ])
Run Code Online (Sandbox Code Playgroud)