如果我使用KVO观察属性,如果观察者是泛型类,则会收到以下错误:
-observeValueForKeyPath:ofObject:change:context:message was 收到但未处理。
以下设置简洁地演示了该问题。定义一些简单的类:
var context = "SomeContextString"
class Publisher : NSObject {
dynamic var observeMeString:String = "Initially this value"
}
class Subscriber<T> : NSObject {
override func observeValueForKeyPath(keyPath: String,
ofObject object: AnyObject,
change: [NSObject : AnyObject],
context: UnsafeMutablePointer<Void>) {
println("Hey I saw something change")
}
}
实例化它们并尝试与订阅者一起观察发布者,就像这样(在空白项目的UIViewController子类中完成):
var pub = Publisher()
var sub = Subscriber<String>()
override func viewDidLoad() {
super.viewDidLoad()
pub.addObserver(sub, forKeyPath: "observeMeString", options: .New, context: &context)
pub.observeMeString = "Now this value"
}
如果我从类定义中删除泛型类型T,那么一切正常,但是否则我得到了#34;收到但未处理错误&#34;。我错过了一些明显的东西吗?还有其他我需要做的事情,还是不应该使用KVO的仿制药?
答案 0 :(得分:5)
通常,有两个原因可以阻止在Objective-C中使用特定的Swift类或方法。
首先,纯Swift类使用C ++样式的vtable dispatch,Objective-C无法理解。在大多数情况下,使用dynamic
关键字可以克服这个问题,正如您显而易见的那样。
第二个是,只要引入了泛型,Objective-C就无法查看泛型类的任何方法,直到它到达祖先不是通用的继承层次结构中的某个点。这包括引入的新方法以及覆盖。
class Watusi : NSObject {
dynamic func watusi() {
println("watusi")
}
}
class Nguni<T> : Watusi {
override func watusi() {
println("nguni")
}
}
var nguni = Nguni<Int>();
当传递给Objective-C时,它将我们的nguni
变量有效地视为Watusi
的实例,而不是Nguni<Int>
的实例,它根本不理解。通过nguni
,Objective-C将打印&#34; watusi&#34; (而不是&#34; nguni&#34;)调用watusi
方法时。 (我说&#34;有效&#34;因为如果你尝试这个并在Obj-C中打印类的名称,它会显示_TtC7Divided5Nguni00007FB5E2419A20
,其中Divided
是我的Swift模块的名称。所以ObjC肯定会意识到这不是Watusi
。)
解决方法是使用隐藏泛型类型参数的thunk。我的实现与您的实现不同,泛型参数表示被观察的类,而不是键的类型。这应该被视为超过伪代码的一步,并且不能充分利用(或深思熟虑)超出获得要点所需的内容。 (但是,我测试了它。)
class Subscriber : NSObject {
private let _observe : (String, AnyObject, [NSObject: AnyObject], UnsafeMutablePointer<Void>) -> Void
required init<T: NSObject>(obj: T, observe: ((T, String) -> Void)) {
_observe = { keyPath, obj, changes, context in
observe(obj as T, keyPath)
}
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
_observe(keyPath, object, change, context)
}
}
class Publisher: NSObject {
dynamic var string: String = nil
}
let publisher = Publisher()
let subscriber = Subscriber(publisher) { _, _ in println("Something changed!") }
publisher.addObserver(subscriber, forKeyPath: "string", options: .New, context: nil)
publisher.string = "Something else!"
这是有效的,因为Subscriber
本身不是通用的,只有init
方法。闭包用于&#34;隐藏&#34; Objective-C中的泛型类型参数。