13

Swift 中计算属性上的 KVO 是否可行?

var width = 0
var height = 0

private var area : Double {
    get {
        return with * height
    }
}

self.addOberser(self, forKeyPath: "area", ......

修改 with 或 height 触发器的客户端代码会观察ValueForKeyPath吗?

只是在进行市长级重构之前进行检查。如果有人手头有答案,KVO 的语法就很烦人,即使是操场也不值得。(我假设答案是否定的)

4

3 回答 3

22

该代码不起作用有两个原因:

  1. 您必须将dynamic属性添加到属性,如Using Swift with Cocoa and Objective-C中“采用 Cocoa 设计模式”area下​​的“键值观察”部分所述。

  2. 您必须声明area依赖,width并且heightKey-Value Observing Programming Guide中的“注册依赖键”中所述。(这适用于 Objective-C 和 Swift。)要使其工作,您还必须dynamic添加widthheight.

    (您可以改为调用willChangeValueForKeydidChangeValueForKey无论何时widthheight更改,但通常更容易实现keyPathsForValuesAffectingArea。)

因此:

import Foundation

class MyObject: NSObject {

    @objc dynamic var width: Double = 0
    @objc dynamic var height: Double = 0

    @objc dynamic private var area: Double {
        return width * height
    }

    @objc class func keyPathsForValuesAffectingArea() -> Set<String> {
        return [ "width", "height" ]
    }

    func register() {
        self.addObserver(self, forKeyPath: "area", options: [ .old, .new ], context: nil)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("observed \(keyPath) \(change)")
    }
}

let object = MyObject()
object.register()
object.width = 20
object.height = 5

输出:

observed Optional("area") Optional([__C.NSKeyValueChangeKey(_rawValue: new): 0, __C.NSKeyValueChangeKey(_rawValue: kind): 1, __C.NSKeyValueChangeKey(_rawValue: old): 0])
observed Optional("area") Optional([__C.NSKeyValueChangeKey(_rawValue: new): 100, __C.NSKeyValueChangeKey(_rawValue: kind): 1, __C.NSKeyValueChangeKey(_rawValue: old): 0])
于 2016-04-11T17:49:00.710 回答
2

正如@Rob 在他的回答中所说,area dynamic从objective-c 中观察到

现在添加willSet { }didSet { }用于widthheight属性,在这两个属性
里面willSet添加这个self.willChangeValueForKey("area")didSet添加self.didChangeValueForKey("area");

area现在每次宽度或高度变化时都会通知观察者。

注意:此代码未经测试,但我认为它应该符合预期

于 2016-04-11T18:20:11.010 回答
2

这些天我通常做的是避免willChangeValueForKey使用didChangeValueForKey未经检查的字符串属性名称来创建计算属性private(set)并创建一个私有func来更新它。

import Foundation

class Foo: NSObject {

    @objc dynamic private(set) var area: Double = 0
    @objc dynamic var width: Double = 0 { didSet { updateArea() } }
    @objc dynamic var height: Double = 0 { didSet { updateArea() } }

    private func updateArea() {
        area = width * height
    }
}
于 2021-02-08T08:38:08.130 回答