概述html
Key-Value-Observe,簡稱KVO,和上節介紹的Notification師出同門,主要目的都是爲了實現觀察者模式。swift
雖然說是同門師兄弟,可是各自精通的技藝倒是各不相同的。併發
不像Notification,KVO沒有所謂「Center」的角色,觀察者和被觀察者之間是直接交互的,沒有第三者插腳。這個特色帶來的最直接的好處就是,KVO比Notification更加的簡單易用。陰陽相隨,利弊相從。正由於KVO沒有「Center」約束,因此當參與觀察和被觀察的角色增多的時候,KVO管理起來就會顯得力不從心了,並且當有大量事件併發執行的時候,NotificationCenter還有整合優化以提升性能的做用,而KVO則沒有這方面的內容(出處)。ide
最後須要注意的一點是,KVO是相伴NSObject的產物,NSObject是Object-C的基類,在swift中,並不是全部的類都繼承了NSObject,這也就意味着並不是全部的類都能用KVO。然而,這些倒並不構成咱們使用KVO的顧慮,畢竟大部分經常使用的類都是繼承NSOject的,咱們大可放心使用KVO,尤爲在觀察對象單個屬性變化方面,KVO絕對是個不可多得的好幫手。函數
簡單點,碼代碼的方式簡單點性能
在上一節Notification中,咱們又是要建立NotificationCenter又是要重寫UILabel,實在是太麻煩了,這節我們簡單點,實現一個和上一節同樣的程序。優化
1 import UIKit 2 3 class ViewController: UIViewController { 4 @IBOutlet weak var passby1: UILabel! 5 @IBOutlet weak var passby2: UILabel! 6 @IBOutlet weak var passby3: UILabel! 7 8 @objc dynamic var passerby1Say:String = "" 9 @objc dynamic var passerby2Say:String = "" 10 @objc dynamic var passerby3Say:String = "" 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 addObserver(self, forKeyPath: #keyPath(passerby1Say), options: [.new], context: nil) 15 addObserver(self, forKeyPath: #keyPath(passerby2Say), options: [.new], context: nil) 16 addObserver(self, forKeyPath: #keyPath(passerby3Say), options: [.new], context: nil) 17 } 18 19 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 20 if keyPath == #keyPath(passerby1Say) { 21 passby1.text = change![NSKeyValueChangeKey.newKey] as! String 22 } else if keyPath == #keyPath(passerby2Say) { 23 passby2.text = change![NSKeyValueChangeKey.newKey] as! String 24 } else if keyPath == #keyPath(passerby3Say) { 25 passby3.text = change![NSKeyValueChangeKey.newKey] as! String 26 } 27 } 28 29 override func didReceiveMemoryWarning() { 30 super.didReceiveMemoryWarning() 31 // Dispose of any resources that can be recreated. 32 } 33 34 @IBAction func flyHeighAction(_ sender: UIButton) { 35 passerby1Say = "路人甲:我不信" 36 passerby2Say = "路人乙:我會信?" 37 passerby3Say = "路人丙:差點信了" 38 } 39 40 deinit { 41 removeObserver(self, forKeyPath: #keyPath(passerby1Say)) 42 removeObserver(self, forKeyPath: #keyPath(passerby2Say)) 43 removeObserver(self, forKeyPath: #keyPath(passerby3Say)) 44 } 45 }
這下夠簡單了吧,代碼部分總過不超過五十行,運行結果和上一節是同樣兒同樣兒的(效果圖)。如今咱們來簡單分析一下。spa
首先,咱們肯定UIViewController是繼承自NSObject的,因此咱們能夠調用「addObserver」函數和重寫「observeValue」函數。code
而後咱們把須要觀察的屬性用addObserver歸入觀察範圍。server
addObserver函數的做用是:調用addObserver函數的對象將一個NSObject的屬性歸入觀察範圍。
此處調用addObserver的是ViewController,因此ViewController是觀察者。
addObserver的第1個參數是被觀察者,此處爲self,因此ViewController又是被觀察的對象。
addObserver的第2個參數是被觀察者的屬性,此處爲passerby1Say/passerby2Say/passerby3Say。passerby1Say/passerby2Say/passerby3Say必須由dynamic關鍵字修飾,表示支持動態觀察,@objc是修飾語句「#keyPath」要求的,用於編譯階段檢查錯誤。
addObserver的第3個參數是1個列表,表示觸發觀察事件屬性,「.new」表示所觀察的屬性改變時,將新值做爲參數傳遞給觀察者;「.old」表示所觀察的屬性改變時,將舊值做爲參數傳遞給觀察者。
addObserver的第4個參數表示觀察事件觸發時所傳遞的參數,通常不多用到,此處置爲nil。
既然咱們已經將所要關注的屬性都歸入觀察範圍了,那麼如今咱們只要關注觀察事件發生時的情形就能夠了。
觀察者經過「obsserveValue」函數接收觀察事件,其中參數change是1個字典,它包含了屬性改變前的舊值或改變後的新值,依據以前調用addObserver時的參數options而定,其餘代碼是不言自明的,此處再也不贅述。
源碼下載:https://pan.baidu.com/s/1TosFFebbSuo6qlKVRuZtxg