Swift中使用KVO

在Swift中使用KVO,有如下两种方法

原OC提供的:

open func addobserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSkeyvalueObservingOptions = [], context: UnsafeMutableRawPointer?)

@available(iOS 5.0, *)
open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String, context: UnsafeMutableRawPointer?)

open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)

使用上面提供的方法实现KVO,需要注意一点,keyPath对应的属性,必须要是OC的属性,如果是通过swift定义的属性,必须在属性添加@objcdynamic修饰才可以,否则KVO将无效果,如下:

class ViewController: UIViewController {
    
    // swift定义的属性,必须使用@objc和dynamic标注,否则kvo将无效果
    @objc dynamic var name: String = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 添加KVO
        addobserver(self, forKeyPath: "name", options: [.new], context: nil)
        
        // 修改name值
        name = "drBox"
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSkeyvalueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("kvo: name: \(change?[.newKey] as? String ?? "")")
    }
}

swift提供的:

func observe<Value>(_ keyPath: KeyPath<ViewController, Value>, options: NSkeyvalueObservingOptions = [], changeHandler: @escaping (ViewController, NSkeyvalueObservedChange<Value>) -> Void) -> NSkeyvalueObservation

使用上面提供的方法实现KVO,注意事项同上,代码如下:

class ViewController: UIViewController {
    
    // swift定义的属性,必须使用@objc和dynamic标注,否则kvo将无效果
    @objc dynamic var name: String = ""
    var obs: NSkeyvalueObservation?
    
    deinit {
        // 移除观察者
        obs = nil
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 添加kvo
        obs = observe(\.name, options: [.new], changeHandler: { (_, change) in
            print("name: \(change.newValue ?? "")")
        })
        
        name = "drBox"
        
    }

}

使用过rxswift都知道,它同样提供了KVO的实现,实现原理实际上就是对OC的KVO的封装,因此在使用时同样要注意以上事项:@objc dynamic 

class ViewController: UIViewController {
    
    @objc dynamic var name: String = ""
    var disposeBag = disposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 方法一:
        rx.observe(\.name).bind { str in
            print("name1: \(str)")
        }.disposed(by: disposeBag)
        // 方法二:
        rx.observe(String.self, "name").map({ $0 ?? ""}).bind { str in
            print("name2: \(str)")
        }.disposed(by: disposeBag)
        // 方法三:
        rx.observeWeakly(String.self, "name").map({ $0 ?? ""}).bind { str in
            print("weak name: \(str)")
        }.disposed(by: disposeBag)
        
        name = "drBox"
        
        
    }

}

其中rx.observe与rx.observeWeakly的区别在于前者返回的Observable会对观察者target强持有(strong);后者返回的Observable会对target弱持有(weak)

具体的比较如下:

性能比较:

  • rx.observe 更加高效,因为它是一个 KVO 机制的简单封装。
  • rx.observeWeakly 执行效率要低一些,因为它要处理对象的释放防止弱引用(对象的 dealloc 关系)。

使用场景比较:

  • 在可以使用 rx.observe 的地方都可以使用 rx.observeWeakly。
  • 使用 rx.observe 时路径只能包括 strong 属性,否则就会有系统崩溃的风险。而 rx.observeWeakly 可以用在 weak 属性上。

 

当然rx.observe也可以决定其返回的Observable是否强持有target,可以设置参数:retainSelf,认:true(强持有)

// retainSelf
rx.observe(String.self, "name", retainSelf: true).map({ $0 ?? ""}).bind { str in
    print("name2: \(str)")
}.disposed(by: disposeBag)

 

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...