53-Swift 之 KVO (Key-Value Observing )

一 、 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  
}

测试结果图

53-Swift 之 KVO (Key-Value Observing )_第1张图片
ACC041EB-F4D8-4EC2-8438-011619B428B5.png

你可能感兴趣的:(53-Swift 之 KVO (Key-Value Observing ))