開篇提醒:OC中的KVO及其KVO的基礎知識可參見:深入runtime探究KVO
Swift中,原本沒有KVO模式,為何這么說咨油,請看下文:
KVO本質(zhì)上是基于runtime
的動態(tài)分發(fā)機制,通過key
來監(jiān)聽value
的值。
OC能夠?qū)崿F(xiàn)監(jiān)聽因為都遵守了NSKeyValueCoding
協(xié)議
OC所有的類都是繼承自NSObject
那先,其默認已經(jīng)遵守了該協(xié)議,但Swift不是基于runtime
的赡艰, Swift 中的屬性處于性能等方面的考慮默認是關閉動態(tài)分發(fā)的售淡,只有在屬性前加 dynamic
才會開啟運行時,允許監(jiān)聽屬性的變化慷垮。
KVO在OC和Swift中的區(qū)別:
OC中揖闸,所有的類繼承自 NSObject
,它對 KVO提供了默認實現(xiàn),但Swift不是料身。
原因有二:
第一:Swift 中繼承自NSObject
的屬性處于性能等方面的考慮汤纸,默認是關閉動態(tài)分發(fā)的, 所以無法使用KVO芹血,不過可以在屬性前加上dynamic
來打開贮泞。
class Person:NSObject {
//name不支持KVO監(jiān)聽,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
}
該類根本無法調(diào)用
open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)
方法幔烛,所以肯定無法使用KVO觀察者模式啃擦,但Swift中提供了屬性觀察器(didSet
,willSet
)來解決這種問題;
class Father: NSObject {
var firstName: String = "First" {
willSet { //新值設置之前被調(diào)用
print("willSet的新值是\(newValue)")
}
didSet { //新值設置之后立即調(diào)用
print("didSet的新值是\(oldValue)")
}
}
}
KVO的正常使用:(“三步走”思想)
第一步:注冊
open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)
第二步:監(jiān)聽
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
第三步:移除
open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)
代碼演示
import UIKit
//監(jiān)聽UISlider的滑動饿悬,把滑動的結(jié)果傳遞給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")
}
}