【问题标题】:KVO on Swift's computed propertiesSwift 计算属性上的 KVO
【发布时间】:2016-08-02 00:23:37
【问题描述】:

在 Swift 中是否可以在计算属性上使用 KVO?

var width = 0
var height = 0

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

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

客户端代码会修改 with 或 height 触发器 observeValueForKeyPath 吗?

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

【问题讨论】:

  • KVO 是关于 setter 的,你的 setter @dhomes 在哪里?
  • 看看RxSwift
  • @Basheer_CAD 这就是重点!我指的是可以观察到只读计算属性吗?设置宽度或高度会触发新的面积计算吗? (假设没有,但也许是一些花哨的编译器???)
  • @dfri,很有趣!非常感谢!
  • @dhomes 看看 Rob 的回答应该会有帮助

标签: ios swift key-value-observing foundation computed-properties


【解决方案1】:

这些天我通常做的是避免 willChangeValueForKeydidChangeValueForKey 带有未经检查的字符串属性名称,是使计算属性 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
    }
}

【讨论】:

    【解决方案2】:

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

    1. 您必须将dynamic 属性添加到area 属性,如“Adopting Cocoa Design Patterns” in Using Swift with Cocoa and Objective-C 下的“键值观察”部分所述。

    2. 您必须声明area 依赖于widthheight,如“Registering Dependent Keys” in the Key-Value Observing Programming Guide 中所述。 (这适用于 Objective-C 和 Swift。)要使其工作,您还必须将 dynamic 添加到 widthheight

      (您可以改为在widthheight 更改时调用willChangeValueForKeydidChangeValueForKey,但通常只实现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])
    

    【讨论】:

    • 我相信根据那些相同的文档链接,您应该指定上下文,不是吗?
    • 出于多种原因,通常使用context 是个好主意,但这个答案与KVO 的那一部分无关。另外,如果你只继承NSObject,除非你愿意,否则你不需要使用context,因为NSObject没有注册任何KVO通知,所以不可能与你的超类发生冲突。
    • 好消息!感谢分享!
    【解决方案3】:

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

    现在为widthheight 属性添加willSet { }didSet { }
    willSet 内为这两个属性添加此self.willChangeValueForKey("area") 并在didSet 添加self.didChangeValueForKey("area");

    现在area 的观察者将在每次宽度或高度发生变化时收到通知。

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

    【讨论】:

      猜你喜欢
      • 2015-02-02
      • 1970-01-01
      • 2016-04-22
      • 2016-09-04
      • 2019-02-14
      • 2018-02-03
      • 2014-11-04
      • 2017-02-27
      • 1970-01-01
      相关资源
      最近更新 更多