一 、 KVO 的概述
-
KVO 的全称 " Key-Value Observing "
-
KVO 是键值观察机制,使得当某个对象特定的属性发生改变时能够通知到别的对象。这经常用于模型和控制器之间的通信。
-
KVO 的主要的优点是你不需要在每次属性改变时手动去发送通知。并且它支持为一个属性注册多个观察者。
二 、KVO 的使用条件
-
被观察的对象,必须准守键值编码。
-
目前 KVO 支持的类型是 NSObject 。
-
被观察的类能够发出属性改变的 KVO 的通知。
-
被监控的类的属性要使用 dynamic 来修饰。否则将不会调用下面方法:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
- 被观察的类要强引用,不能清除。
三 、 KVO 的注册观察者的方法及参数介绍
open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)
1、该方法的参数介绍
-
observer : 一个注册的KVO 对象,必须满足 KVC 编码特性。
-
keyPath : 一个注册的关键路径,不能为空。
-
options : 指定包含在观察是什么通知。
-
context : 这个参数可以是一个 C 指针或者是一个 对象引用,它可以作为这个context的唯一标识,也可以提供一些数据给观察者。
2、NSKeyValueObservingOptions 的取值介绍
-
initial : 在注册观察者的方法 return 的时候就发出一次通知。
-
new :表示Options里面有新的值时,发送一次通知。
-
old : 表示Options里面含所有属性变化前的值。
-
prior : 会在值发生改变前发出一次通知,当然改变后的通知依旧还会发出,也就是每次change都会有两个通知。
四 、 KVO 响应观察者的方法及参数的介绍
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
该方法的参数介绍
-
keyPath : 注册观察者时的关键路径。
-
object : 被注册观察者的对象。
-
change : change 是一个字典,它里面包含了的信息由注册时的 options 决定。
-
context : 这个参数可以是一个 C 指针或者是一个 对象引用,它可以作为这个context的唯一标识,也可以提供一些数据给观察者。
五、 KVO 的注销方法及参数介绍
open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String, context: UnsafeMutableRawPointer?)
或者
open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)
参数的介绍
-
observer :注册的观察者。
-
keyPath : 注册观察者时的关键路径。
-
context :一个在注册观察者时的一个 C 指针或者是一个 对象引用,它可以作为这个context的唯一标识,也可以提供一些数据给观察者。
六、 KVO 的使用举例
1> 我们首先创建一个 Person 类。
/**
创建一个类
*/
class Person : NSObject{
dynamic var name : String?
var firstName : String?
var lastName : String?
/**
获取用户的名字
*/
func getPersonName() -> String {
name = firstName! + lastName!
return name!
}
/**
反初始化
*/
deinit {
print("反初始化完成")
}
}
2> 注册观察者
/**
创建一个类
*/
NewPerson = Person.init()
/**
开始注册观察者
*/
NewPerson.addObserver(self, forKeyPath: "name", options: .new, context: &NewContext)
NewPerson.firstName = "周"
NewPerson.lastName = "NetWork小贱"
/**
获取名字
*/
let NewName = NewPerson.getPersonName()
MyName = NewName
3> 观察者的响应事件
/**
观察者方法的实现
*/
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath=="name" {
print("sss")
print(object!)
print(context!)
print(change!)
}else{
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
4> 当我们观察的类不存在该属性的异常处理
如果Person 类中没有我们要设定的这个属性,则 KVC 也无法找到这个属性值,这时候 KVC 协议其实会调用 valueForUndefinedKey 方法,NSObject 对这个方法的默认实现是抛出一个 NSUndefinedKeyException 异常。
该异常的解决方法
/**
没有key时的异常处理
*/
override func value(forUndefinedKey key: String) -> Any? {
return ""
}
则最使用的方法是在观察类中加入上面的方法,则观察类的写法如下:
/**
创建一个类
*/
class Person : NSObject{
dynamic var name : String?
var firstName : String?
var lastName : String?
/**
获取用户的名字
*/
func getPersonName() -> String {
name = firstName! + lastName!
return name!
}
/**
没有key时的异常处理
*/
override func value(forUndefinedKey key: String) -> Any? {
return ""
}
/**
反初始化
*/
deinit {
print("反初始化完成")
}
}
七 、属性观察器的介绍(再次介绍)
1> 介绍
属性观察器相当于內建的KVO,监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。
2> 属性检查其的检测方式
-
willSet在设置新的值之前调用。
-
didSet在新的值被设置之后立即调用。
注释
willset观察器会将新的属性值作为固定参数传入,在willSet的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称newValue表示。类似地,didSet观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。
注意
willSet和didSet观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。
3> 举例介绍
/**
属性观察器
*/
func attributeViewer() -> Void {
var PersonCount:Int = 0 {
willSet(newValue){
print("新人数是:" + "\(newValue)" + "个")
}
didSet(oldValue){
print("老人数是:" + "\(oldValue)" + "个")
}
}
/**
测试
*/
PersonCount = 10
}
测试结果图
ACC041EB-F4D8-4EC2-8438-011619B428B5.png
网友评论