2017-06-22 42 views
4

我试图在WWDC 2017基金会谈话中为KVO观察工作获得与示例非常相似的内容。我所看到的与那次演讲不同的唯一区别是,我必须调用super.init(),并且必须隐式地解开“kvo”令牌。Swift 4(BETA 2)基于WWDC对话的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 

此错误:

fatal error: Could not extract a String from 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, line 85

而且,看到此错误:

error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

是任何人都能够得到这样的这工作,或者这是我需要报告的错误?

回答

6

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

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

Nodestruct,所以不能在OBJ - C的直接代表。但是,编译器仍允许您将tr标记为dynamic - 其中要求@objc。虽然@objcMembers推断为类的成员@objc,但它只适用于可直接在Obj-C中表示的成员,而tr不是。

所以真的,编译器不应该让你把tr标记为dynamic - 我继续和filed a bug here

tr需要@objc & dynamic你使用就可以了志愿,因为志愿要求的方法混写,其中对象 - C运行时提供,和斯威夫特运行时不。因此,要使用KVO这里你需要做一个Nodeclass,并从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的视频,你会看到他们正在观察属性是类型classNSObject继承)

然而,在你给的例子其实你并不真正需要志愿 - 你可以只保留Nodestruct,而是使用财产观察员:

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: [:]) 
// ]) 
+0

谢谢。我知道didSet,但我正在尝试新的KVO的东西。 – possen

+0

所有伟大的,除了编译器不抱怨,我们有一个运行时崩溃(可能在运输后) – Jonny

0

据苹果公司称,这是在这个时候预期的行为,因为它依赖于目标-C运行时。这是他们对我的错误报告的回应,它进一步证实了接受的答案海报所说的。