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

时间:2017-06-22 18:08:51

标签: swift key-value-observing

我试图得到一些非常类似于WWDC 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

此错误:

  

致命错误:无法从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   (code = EXC_I386_INVOP,subcode = 0x0)。这个过程一直留在了   它被中断的点,使用"线程返回-x"回到   表达评估前的状态。

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

3 个答案:

答案 0 :(得分:17)

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

@objcMembers class MyController : NSObject {
    dynamic var tr: Node
    // ...

Nodestruct,因此无法直接在Obj-C中表示。但是,编译器仍允许您将tr标记为dynamic - 需要 @objc。虽然@objcMembers为类成员推断@objc,但只有在Obj-C中可直接表示的成员才会这样做,tr不是。

所以真的,编译器不应该让你将tr标记为dynamic - 我继续filed a bug herehas now been fixed并且将为Swift做好准备5。

tr 需要@objc& dynamic让你在它上面使用KVO,因为KVO需要方法调配,Obj-C运行时提供,而Swift运行时没有。因此,要在此使用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
    }
}

(如果您再次查看WWDC视频,您会看到他们正在观察的属性实际上是继承自class的{​​{1}}类型的属性

但是,在您提供的示例中,您并不真正需要KVO - 您可以将NSObject保留为Node,而是使用属性观察者:

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: [:]) 是值类型,Node也会触发其属性的任何更改:

didSet

答案 1 :(得分:1)

根据Apple的说法,这是目前的预期行为,因为它取决于Objective-C运行时。这是他们对我的错误报告的回应,它进一步证实了接受的答案海报所说的内容。

答案 2 :(得分:0)

接受的答案是正确的。但我想在Swift中说出我对KVO的了解。

Swift还在许多Kit和实现中混合编译OC,例如KVO。所以,你应该知道如何在OC中实现KVO。

对象addObserver: forKeyPath:时,OC运行时创建子类继承对象所属的类,然后在对象更改时重写对象的setter方法,它会调用settersetValue(_ value: Any?, forKey key: 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的了解,我将不断搜索并更新我的答案。