Swift 4新知:KVC和KVO新姿勢

沒故事的卓同學發表於2017-07-11

隨著 keypath 得到增強,KVC 和 KVO 的 API 都有了一些進化。

struct 也支援 KVC

一個感人的進步就是 struct 也支援 KVC 了。但是並不是使用原有的setValue:forKeypath的api。而是利用了swfit 4增加的一個語法特性:自定義索引可以有引數名。
直接上程式碼吧:

struct ValueType {
    var name:String
}

var object = ValueType(name: "zhuo")
let name = \ValueType.name

// set
object[keyPath: name] = "swift4"
// get
let valueOfName = object[keyPath:name]複製程式碼

通過索引可以方便的進行KVC。

KVO

遺憾的是依然只有 NSObject 才能支援 KVO。

Swift 4中的一個對此有影響的改變是繼承 NSObject 的 swift class 不再預設全部 bridge 到 OC。原因可以參考我的前一篇部落格:Swift 4新知:自動清除冗餘程式碼減小包大小。然而 KVO 又是一個純 OC 的特性,所以如果是 swift class 需要在宣告的時候增加 @objcMembers 關鍵字。否則在執行的時候你會得到一個 error:

fatal error: Could not extract a String from KeyPath Swift.ReferenceWritableKeyPath

另外一件事就是被觀察的屬性需要用dynamic修飾,否則也無法觀察到。

一個好訊息是不需要在物件被回收時手動 remove observer。但是這也帶來了另外一個容易被忽略的事情:觀察的閉包沒有被強引用,需要我們自己新增引用,否則當前函式離開後這個觀察閉包就會被回收了。

@objcMembers class OCClass: NSObject {
    dynamic var name: String

    init(name: String) {
        self.name = name
    }
}

class ViewController: UIViewController {

    var swiftClass: OCClass!
    var ob: NSKeyValueObservation!

    override func viewDidLoad() {
        super.viewDidLoad()

        swiftClass = OCClass(name: "oc")
        ob = swiftClass.observe(\.name) { (ob, changed) in
            let new = ob.name
            print(new)
        }
        swiftClass.name = "swift4"
    }
}複製程式碼

KVO 之後返回的是一個 NSKeyValueObservation 例項,需要自己控制這個例項的生命週期。

參考:
Key Value Observation in iOS 11
smart key path
Re-enabling @objc inference within a class hierarchy

歡迎關注我的微博:@沒故事的卓同學

相關文章