OC基礎-(四)KVC、KVO

weixin_34292287發表於2018-11-02

KVO

Q: 什麼是KVO?

  • KVO(key-value observing),從名稱上就可以知道這是一種鍵值觀察的機制
  • KVO是OC對觀察者模式的又一實現
  • Apple使用isa混寫技術(isa-swizzling)來實現KVO

當我們註冊一個物件的觀察者的時候,也就是呼叫了系統的addObserver:forKeyPath:options:context:方法,來觀察一個物件的某個屬性,系統會在執行時建立一個新的物件NSKVONotifying_物件類名,並將原本指向物件類的isa指標指向這個NSKVONotifying_物件類名NSKVONotifying_物件類名實際上是原物件類的子類,只是我們在這個子類中,重寫了需要觀察的屬性的set方法。以此達到觀察值變化的目的,如下圖所示:

8037794-9e61773e0918c69b.png
Xnip2018-10-19_21-50-35.png

Q: 那麼,為什麼系統新新增的set方法就能夠監聽到值變化呢?

系統為我們實現的set方法中有這兩個方法:

  • - (void)willChangeValueForKey:(NSString *)key
  • - (void)didChangeValueForKey:(NSString *)key

這兩個方法分將對屬性賦值的程式碼包了起來

- (void)setValue:(id)obj {
    [self willChangeValueForKey:@"keyPath"];
    // 呼叫父類的實現, 也即是原類的實現
    [super setValue:obj];
    [self didChangeValueForKey:@"keyPath"];
}

Q: 通過KVC設定的value,KVO能否監聽到變化?

答案是能!那麼為什麼會KVC設定的值,會生效呢?實際上就是要因為在賦值時會呼叫到屬性的set方法,所以會使得KVO被觸發。

Q: 通過成員變數直接賦值value,KVO能否有效?

答案是不能,系統為我們提供的KVO實際上還是對set方法的覆蓋,但我們直接對成員變數賦值是不會走set方法的。但是,我們可以手動的實現KVO,也就是將上面的willChangeValueForKey:didChangeValueForKey:按照上面的樣子。將設定值的程式碼包起來即可。

KVC

Q: 什麼是KVC

  • KVC是鍵值編碼(key-value coding)的縮寫,和鍵值編碼相關的方法
    • - (id)valueForKey:(NSString *)key,這個方法可以獲取和key同名,或者相似名稱的屬性值,並返回
    • - (void)setValue:(id)value forKey:(NSString *)key 可以獲取和這個key同名或者相似名稱的屬性賦值

我們現在來看看KVC的獲取屬性的流程

8037794-cf7c82302df6a1bd.png
Xnip2018-10-19_22-37-59.png
  1. 首先會判斷我們使用的key(或相似)是否有get方法,如果get方法存在,則直接呼叫,結束訪問流程
  2. 如果訪問的key的get方法未找到,則會判斷是否有同名或相似的例項變數存在,如果存在,則返回
  3. 在get方法未找到的過程中,系統給我們提供了一個可遮蔽的開關+ (BOOL)accessInstanceVariablesDirectly,如果和key相同或同名的例項變數存在,則返回YES,但是如果我們重寫這個方法並返回NO,即使這個同名例項變數存在,也不會被獲取到。
  4. 如果例項變數不存在,系統會呼叫當前例項的valueForUndefinedKey:方法,然後丟擲NSUndefinedKeyException異常,結束呼叫流程。

這裡有兩個點需要注意:

  • 訪問器方法(Accessor Method)是否存在的判斷規則時什麼樣的
  • 例項變數(Instance var)是否存在的規則時什麼樣的

訪問器方法(Accessor Method)

訪問器方法也實現了相似的概念,但是方法的命名必須遵循駝峰命名下面這幾種是可以被判斷為實現了key的訪問器方法:

  • getKey
  • key
  • isKey

例項變數(Instance ver)判斷

下面幾種例項變數的名稱是可以被識別的

  • _key
  • _isKey
  • key
  • isKey

再讓我們現在來看看KVC的Set方法的流程

8037794-0628192ae43c224e.png
Xnip2018-10-20_21-48-16.png

其方法查詢流程,與get的流程相同

相關文章