簡體   English   中英

KVO觀察不適用於Swift泛型

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM