swift中KVO和屬性觀察器

開篇提醒:OC中的KVO及其KVO的基礎知識可參見:深入runtime探究KVO

Swift中,原本沒有KVO模式,爲何這麼說,請看下文:

KVO本質上是基於runtime的動態分發機制,通過key來監聽value的值。
OC能夠實現監聽因爲都遵守了NSKeyValueCoding協議
OC所有的類都是繼承自NSObject,其默認已經遵守了該協議,但Swift不是基於runtime的, Swift 中的屬性處於性能等方面的考慮默認是關閉動態分發的,只有在屬性前加 dynamic纔會開啓運行時,允許監聽屬性的變化。

KVO在OC和Swift中的區別:

OC中,所有的類繼承自 NSObject ,它對 KVO提供了默認實現,但Swift不是。
原因有二:

第一:Swift 中繼承自NSObject的屬性處於性能等方面的考慮,默認是關閉動態分發的, 所以無法使用KVO,不過可以在屬性前加上dynamic來打開。

class Person:NSObject {
//name不支持KVO監聽,age支持KVO
var name: String
dynamic var age: Int = 0            

init(name: String,age: Int) {
    self.name = name
    self.age =age
}

第二: 不是所有的類都繼承自NSObject,不繼承自的對象

譬如:

class Person {
var name: String?
var age: Int = 0

init(name: String,age: Int) {
    self.name = name
    self.age =age
}

該類根本無法調用

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

方法,所以肯定無法使用KVO觀察者模式,但Swift中提供了屬性觀察器(didSet,willSet)來解決這種問題;

class Father: NSObject {

var firstName: String = "First" {
        willSet {   //新值設置之前被調用
            print("willSet的新值是\(newValue)")
        }
        didSet { //新值設置之後立即調用
            print("didSet的新值是\(oldValue)")
        }
    }
 }

KVO的正常使用:(“三步走”思想)

第一步:註冊
    open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)
    
第二步:監聽
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) 
    
第三步:移除
    open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)

代碼演示


import UIKit

//監聽UISlider的滑動,把滑動的結果傳遞給UIProgressView,以顯示滑動進度

class viewController: UIViewController {
    @IBOutlet weak var slider: UISlider!
    @IBOutlet weak var progressView: UIProgressView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        self.progressView.addObserver(self, forKeyPath: "progress", options: .new, context: nil)
        self.slider.addObserver(self, forKeyPath: "value", options: .new, context: nil)
    }

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

        if keyPath == "value" {
            if let value = change?[NSKeyValueChangeKey.newKey] as? Float {
                self.progressView.progress = value/self.slider.maximumValue
                view.alpha = CGFloat(self.progressView.progress)
                self.textLabel.font = UIFont.systemFont(ofSize: view.alpha * 20)
                self.textField.text = self.father?.firstName
            }
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
  
    deinit {
        self.progressView.removeObserver(self, forKeyPath: "progress")
        self.slider.removeObserver(self, forKeyPath: "value")
    }
}

詳細Demo的GitHub地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章