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  
}

測試結果圖

ACC041EB-F4D8-4EC2-8438-011619B428B5.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,933評論 18 139
  • 或者KVO,是一個非正式協議,它定義了對象之間觀察和通知狀態改變的通用機制。 基本使用 使用KVO必須要滿足的條件...
    一枚iOS程序猿閱讀 757評論 0 0
  • 上半年有段時間做了一個項目,項目中聊天界面用到了音頻播放,涉及到進度條,當時做android時候處理的不太好,由于...
    DaZenD閱讀 3,045評論 0 26
  • ??KVO簡介:鍵值觀察,是基于鍵值編碼(KVC)的一種觀察模式。 是iOS中常用的一種消息傳遞機制。??對接模...
    西葉lv閱讀 474評論 0 2
  • [深入淺出Cocoa]詳解鍵值觀察(KVO)及其實現機理羅朝輝 (http://www.cppblog.com/k...
    Crazy2015閱讀 708評論 0 1