chr*_*838 6 generics key-value-observing ios swift
如果我使用KVO观察属性,如果观察者是泛型类,那么我收到以下错误:
-observeValueForKeyPath:ofObject:change:context:收到消息但未处理.
以下设置简洁地演示了该问题.定义一些简单的类:
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")
}
}
Run Code Online (Sandbox Code Playgroud)
实例化它们并尝试与订阅者一起观察发布者,就像这样(在空白项目的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"
}
Run Code Online (Sandbox Code Playgroud)
如果我从类定义中删除泛型类型T然后一切正常,但否则我得到"收到但未处理的错误".我错过了一些明显的东西吗?还有其他我需要做的事情,还是仿制品不应该与KVO合作?
通常,有两个原因可以阻止在Objective-C中使用特定的Swift类或方法.
第一个是纯Swift类使用C++样式的vtable调度,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>();
Run Code Online (Sandbox Code Playgroud)
当传递给Objective-C时,它将我们的nguni变量有效地视为一个实例Watusi,而不是Nguni<Int>它根本不理解的实例.通过a nguni,Objective-C将在watusi调用方法时打印"watusi"(而不是"nguni").(我说"有效",因为如果你试试这个,打印ObjC类的名称,它表明_TtC7Divided5Nguni00007FB5E2419A20,在那里Divided是我的雨燕模块的名称,所以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!"
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为Subscriber它本身不是通用的,只有它的init方法.闭包用于从Objective-C"隐藏"泛型类型参数.