【问题标题】:KVO: How to get old / new values in observeValue(forKeyPath:...) in Swift?KVO:如何在 Swift 中的 observeValue(forKeyPath:...) 中获取旧值/新值?
【发布时间】:2014-09-21 12:03:04
【问题描述】:

我创建了一个带有.Old | .New 选项的观察者。在处理程序方法中,我尝试获取 before after 值,但编译器抱怨:'NSString' is not convertible to 'NSDictionaryIndex: NSObject, AnyObject

override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [NSObject : AnyObject]!, context: UnsafeMutablePointer<Void>) {

    let approvedOld = change[NSKeyValueChangeOldKey] as Bool
    let approvedNew = change[NSKeyValueChangeNewKey] as Bool

【问题讨论】:

    标签: swift key-value-observing


    【解决方案1】:

    iOS 11 和 Swift >4.1

    iOS 11 和 Swift 4 为 KVO 带来了重大变化。

    • 类应采用@objcMembers 注解以启用KVO 或KVO 静默失败。
    • 要观察的变量必须声明为dynamic

    这是较新的实现,

    @objcMembers
    class Approval: NSObject {
    
        dynamic var approved: Bool = false
    
        let ApprovalObservingContext = UnsafeMutableRawPointer(bitPattern: 1)
    
        override init() {
            super.init()
    
            addObserver(self,
                        forKeyPath: #keyPath(approved),
                        options: [.new, .old],
                        context: ApprovalObservingContext)
        }
    
        override func observeValue(forKeyPath keyPath: String?,
                                   of object: Any?,
                                   change: [NSKeyValueChangeKey : Any]?,
                                   context: UnsafeMutableRawPointer?) {
            guard let observingContext = context,
                observingContext == ApprovalObservingContext else {
                    super.observeValue(forKeyPath: keyPath,
                                       of: object,
                                       change: change,
                                       context: context)
                    return
            }
    
            guard let change = change else {
                return
            }
    
            if let oldValue = change[.oldKey] {
                print("Old value \(oldValue)")
            }
    
            if let newValue = change[.newKey]  {
                print("New value \(newValue)")
            }
    
        }
    
        deinit {
            removeObserver(self, forKeyPath: #keyPath(approved))
        }
    }
    

    还有新的基于 Bock 的 KVO api,它的工作原理是这样的,

    @objcMembers
    class Approval: NSObject {
    
        dynamic var approved: Bool = false
    
        var approvalObserver: NSKeyValueObservation!
    
        override init() {
            super.init()
            approvalObserver = observe(\.approved, options: [.new, .old]) { _, change in
                if let newValue = change.newValue {
                    print("New value is \(newValue)")
                }
    
                if let oldValue = change.oldValue {
                    print("Old value is \(oldValue)")
                }
            }
    
        }
    }
    

    基于块的 api 看起来超级好且易于使用。另外,KeyValueObservation 在 deinited 时是无效的,因此移除观察者没有硬性要求。

    Swift 2.0 和 iOS

    在 Swift 2.0 中,这是一个使用 KVO 的类的完整实现,

     class Approval: NSObject {
    
        dynamic var approved: Bool = false
    
        let ApprovalObservingContext = UnsafeMutablePointer<Int>(bitPattern: 1)
    
        override init() {
            super.init()
            addObserver(self, forKeyPath: "approved", options: [.Old, .New], context: ApprovalObservingContext)
        }
    
        override func observeValueForKeyPath(keyPath: String?,
                                             ofObject object: AnyObject?,
                                                      change: [String : AnyObject]?,
                                                      context: UnsafeMutablePointer<Void>) {
    
            if let theChange = change as? [String: Bool] {
    
                if let approvedOld = theChange[NSKeyValueChangeOldKey]  {
                    print("Old value \(approvedOld)")
                }
    
                if let approvedNew = theChange[NSKeyValueChangeNewKey]{
                    print("New value \(approvedNew)")
    
                }
    
                return
            }
            super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
        }
    
        deinit {
            removeObserver(self, forKeyPath: "approved")
        }
    }
    
    let a  = Approval()
    a.approved = true
    

    【讨论】:

    • 此代码无法编译。错误:[String : AnyObject]?' is not convertible to '[NSString : Bool]'
    • 太棒了!非常感谢修复!
    猜你喜欢
    • 2018-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-26
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    相关资源
    最近更新 更多