聊聊KVO、KVC(僅用作自己復(fù)習(xí)記錄不喜勿噴)
KVC:Key Value Coding 鍵值編碼
KVO:Key Value Observer 鍵值監(jiān)聽
可以先從KVO開始講
我們開發(fā)中時(shí)常會(huì)需要對(duì)某個(gè)對(duì)象的某個(gè)屬性值的變化進(jìn)行監(jiān)聽,如果這個(gè)值發(fā)生遍話,就拿到這個(gè)事件,并做我們自己的邏輯處理。比如一個(gè)Person對(duì)象,內(nèi)有一個(gè)int a屬性,當(dāng)a的值放生變化時(shí),我們就可以感知到這個(gè)事件。
它的底層實(shí)現(xiàn),其實(shí)是在添加監(jiān)聽者的時(shí)候,由runtime 動(dòng)態(tài)的創(chuàng)建了一個(gè)Person子類,并且重寫了該子類的setAge:方法,在重寫這個(gè)方法時(shí),分了幾個(gè)步驟:
1、willChangeValueForKey: ?
2、_age = age ?
3、didChangeValueForKey:?
內(nèi)部會(huì)觸發(fā)監(jiān)聽器(Observer)的監(jiān)聽方法(observeValueForKeyPath:ofObject:context:)
根據(jù)上述底層原理,可以推出以下結(jié)論,當(dāng)然也可以寫demo來驗(yàn)證
1、如果希望手動(dòng)觸發(fā)KVO,只需要手動(dòng)調(diào)用willChangeValueForKey: ?和didChangeValueForKey:?
2、如果通過->箭頭函數(shù)修改對(duì)象的成員變量值,不會(huì)觸發(fā)KVO,因?yàn)檫@種方式并沒有調(diào)用到set方法。
另外KVO還有個(gè)缺點(diǎn),就是我們?cè)趯懕O(jiān)聽成員變量字段的時(shí)候,必須使用字符串來寫,這樣的話,系統(tǒng)就沒有提示了,如果寫錯(cuò)了,也不會(huì)有報(bào)錯(cuò)提示,這樣會(huì)存在一定的隱患。
如何優(yōu)化這個(gè)問題呢?
1、比如我們想監(jiān)聽age,之前的寫法是@"age",那么如果我改成person.age這種就可以了。當(dāng)然還有參數(shù)類型,需要知道這個(gè)參數(shù)是int float 還是NSString、或者其他。 ?那我們可以參考沙盒的方式,setInteger:forKey:,setFloat:forKey:,setObject: forKey:. ?
2、如果采取這種方式,接口可能太多了??梢栽囋囃ㄟ^runtime的接口?ivar_getTypeEncoding ? ?ivar_getName ??class_copyIvarList 這種獲取成員變量列表,以及獲取成員變量類型、然后進(jìn)行比對(duì),的方式來獲取傳入值的類型,知道類型以后,后續(xù)的操作,就可以按照1中的方式來做了。
下面再講講KVC的
KVC我們常用到的就是取值valueForKey和 設(shè)置值setValueForKey,比如我們想通過KVC更改person對(duì)象中age的值,那么就可以調(diào)用 [person setValue@18 ForKey@"age"]; 其實(shí)這里底層也是調(diào)用的set方法,拿到age字符串之后,先把第一個(gè)字母變大寫,然后前面拼接 @"set", 變成set方法 setAge:。 可以試試,通過KVC的修改age,是可以觸發(fā)KVO事件的。 然后取值,valueForKey也是同樣原理調(diào)用的get方法
