[英]KVO observation not working with Swift generics
如果我使用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")
}
}
實例化它們並嘗試與訂閱者一起觀察發布者,就像這樣(在空白項目的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然后一切正常,但否則我得到“收到但未處理的錯誤”。 我錯過了一些明顯的東西嗎? 還有其他我需要做的事情,還是仿制品不應該與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>();
當傳遞給Objective-C的,它認為我們的nguni
有效變量的一個實例Watusi
,而不是一個實例Nguni<Int>
,它不明白的。 通過一個nguni
,當調用watusi
方法時,Objective-C將打印“watusi”(而不是“nguni”)。 (我說“有效”,因為如果你嘗試這個並在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
方法。 閉包用於從Objective-C“隱藏”泛型類型參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.