概述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