Swift 4 (BETA 2) KVO 崩溃,基于 WWDC 谈话
Swift 4 (BETA 2) KVO crashing, based upon WWDC talk
我正在尝试获得与 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
这个错误:
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.
有没有其他人能够得到这样的东西,或者这是一个我需要报告的错误?
这里的错误是编译器让你说:
@objcMembers class MyController : NSObject {
dynamic var tr: Node
// ...
Node
是一个 struct
,所以不能在 Obj-C 中直接表示。但是,编译器仍然允许您将 tr
标记为 dynamic
– 需要 @objc
。虽然 @objcMembers
为 class 的成员推断出 @objc
,但它仅对可以在 Obj-C 中直接表示的成员这样做,而 tr
则不能。
所以真的,编译器不应该让你将 tr
标记为 dynamic
– 我继续 filed a bug here, which has 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 视频,您会看到他们观察到的 属性 实际上是继承自 [=29= 的 class
类型])
但是,在您给出的示例中,您实际上并不需要 KVO – 您可以将 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
也会触发对其属性的任何更改:
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: [:])
// ])
根据 Apple 的说法,这是目前的预期行为,因为它取决于 Objective-C 运行时。这是他们对我的错误报告的回应,它进一步证实了已接受的答案发布者所说的话。
接受的答案是正确的。但是我想在Swift.
说说我对KVO的了解
Swift
也在许多 Kit 和实现中混合编译 OC
,例如 KVO。所以,你应该知道 OC
.
中 KVO 是如何实现的
当你addObserver: forKeyPath:
一个对象时,OC
运行时创建一个子class继承对象所属的class,然后重写setter
对象的方法,当对象改变时,它调用setter
和setValue(_ 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的了解,我会不断搜索更新我的答案。
我正在尝试获得与 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
这个错误:
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.
有没有其他人能够得到这样的东西,或者这是一个我需要报告的错误?
这里的错误是编译器让你说:
@objcMembers class MyController : NSObject {
dynamic var tr: Node
// ...
Node
是一个 struct
,所以不能在 Obj-C 中直接表示。但是,编译器仍然允许您将 tr
标记为 dynamic
– 需要 @objc
。虽然 @objcMembers
为 class 的成员推断出 @objc
,但它仅对可以在 Obj-C 中直接表示的成员这样做,而 tr
则不能。
所以真的,编译器不应该让你将 tr
标记为 dynamic
– 我继续 filed a bug here, which has 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 视频,您会看到他们观察到的 属性 实际上是继承自 [=29= 的 class
类型])
但是,在您给出的示例中,您实际上并不需要 KVO – 您可以将 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
也会触发对其属性的任何更改:
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: [:])
// ])
根据 Apple 的说法,这是目前的预期行为,因为它取决于 Objective-C 运行时。这是他们对我的错误报告的回应,它进一步证实了已接受的答案发布者所说的话。
接受的答案是正确的。但是我想在Swift.
说说我对KVO的了解Swift
也在许多 Kit 和实现中混合编译 OC
,例如 KVO。所以,你应该知道 OC
.
当你addObserver: forKeyPath:
一个对象时,OC
运行时创建一个子class继承对象所属的class,然后重写setter
对象的方法,当对象改变时,它调用setter
和setValue(_ 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的了解,我会不断搜索更新我的答案。