1、KVC
KVC可以通過key直接訪問對象的屬性,或者給獨享的屬性直接賦值,這樣可以在運行時動態(tài)的訪問或修改對象的屬性。
當調(diào)用setValue:forKey: setValue:forKeyPath:時,底層的執(zhí)行機制如下:
1. 程序首先按照`setKey:`、`_setKey:`、`setIsKey:`順序查找方法,如果找到直接調(diào)用該方法,結束。
2. 如果沒有找到,????????????????則會查看`+(BOOL)accessInstanceVariablesDirectly`方法的返回值:
2.1. 如果返回`NO`,表示不允許訪問成員變量,則直接執(zhí)行`setValue:forUndefinedKey:`方法拋出異常,結束。
2.2. 如果返回`YES`,表示允許訪問成員變量。
3. 如果允許訪問成員變量,則按照 `_key`、`_isKey`、`key`、`isKey` 的順序查找成員變量,如果找到了成員變量,則直接賦值,結束。
4. 如果沒有仍然沒有找到成員變量,則直接執(zhí)行`setValue:forUndefinedKey:`方法拋出異常,結束。
當調(diào)用valueForKey:、valueForKeyPath:時,底層的執(zhí)行機制如下:
1. 程序首先按照`getKey`、`key`、`isKey`、`_getKey`、`_key`順序查找方法,如果找到直接調(diào)用該方法,結束。
2. 如果沒有仍然沒有找到以上方法,則會調(diào)用一些動態(tài)生成的集合方法(這些方法可以可重新定義KVC的一些功能):
2.1. 如果同時存在`countOf<Key>`和[`objectIn<Key>AtIndex:` `<Key>AtIndexes:`]中的一個方法,
則返回一個可以響應NSArray所有方法的代理集合
還有一個可選方法 `get<Key>:range:`
2.2. 如果同時存在 `countOf<Key>`,`enumeratorOf<Key>`,`memberOf<Key>:` 三個方法,
則返回一個可以響應NSSet所有方法的代理集合
3. 如果沒有找到,????????????????則會查看`+(BOOL)accessInstanceVariablesDirectly`方法的返回值:
3.1. 如果返回`NO`,表示不允許訪問成員變量,則直接執(zhí)行`valueForUndefinedKey`方法拋出異常,結束。
3.2. 如果返回`YES`,表示允許訪問成員變量。
4. 如果允許訪問成員變量,則按照`_key`、`_isKey`、`key`、`isKey`的順序查找成員變量,如果找到了成員變量,則直接賦值,結束。
5. 如果沒有仍然沒有找到成員變量,則直接執(zhí)行`valueForUndefinedKey:`方法拋出異常,結束。
2、KVO
KVO全稱KeyValueObserving,是蘋果提供的一套事件通知機制。允許對象監(jiān)聽另一個對象特定屬性的改變,并在改變時接收到事件。由于KVO的實現(xiàn)機制,所以對屬性才會發(fā)生作用,一般繼承自NSObject的對象都默認支持KVO
實現(xiàn)原理
-
KVO是通過isa-swizzling技術實現(xiàn)的。 - 在運行時根據(jù)原類創(chuàng)建一個中間類
NSKVONotifying_KVOObject,這個中間類是原類的子類,并動態(tài)修改當前對象的isa指向中間類。并且將class方法重寫,返回原類的Class。
在 - 重寫被監(jiān)聽屬性的
setter方法:在重寫的setter方法中,修改值之前會調(diào)用willChangeValueForKey:方法,修改值之后會調(diào)用didChangeValueForKey:方法,這兩個方法最終都會被調(diào)用到observeValueForKeyPath:ofObject:change:context:方法中
面試題
1、使用KVC設值能觸發(fā)KVO嗎?直接修改成員變量呢?
會觸發(fā)KVO,KVC內(nèi)部會調(diào)用 `willChangeValueForKey:` 和 `didChangeValueForKey:` 方法
直接修改修改成員變量不會觸發(fā) KVO
2、iOS用什么方式實現(xiàn)對一個對象的KVO?(KVO的本質是什么?)
當一個對象使用了KVO監(jiān)聽,iOS系統(tǒng)會修改這個對象的isa指針,改為指向一個全新的通過Runtime動態(tài)創(chuàng)建的子類
子類擁有自己的set方法實現(xiàn),內(nèi)部會調(diào)用
willChangeValueForKey:
原來的setter
didChangeValueForKey:, 這個方法的內(nèi)部又會調(diào)用監(jiān)聽器observer的方法
3、如何手動出發(fā)KVO?
手動調(diào)用這兩個方法,必須兩個方法同時有
willChangeValueForKey:
didChangeValueForKey: