本系列是學習iOS底層原理過程中的記錄筆記第三篇,往期目錄:
探索iOS底層原理開篇——對象本質
探索iOS底層原理第二篇——KVO
國際慣例先拋出面試題:
- 通過KVC修改屬性會觸發KVO么?
- KVC的賦值和取值過程是怎樣的?原理是什么?
分析過程:
KVC的全稱是Key-Value Coding
,俗稱“鍵值編碼”,可以通過一個key來訪問某個屬性
常見的API有
(void)setValue:(id)value forKeyPath:(NSString *)keyPath;
(void)setValue:(id)value forKey:(NSString *)key;
(id)valueForKeyPath:(NSString *)keyPath;
(id)valueForKey:(NSString *)key;
新建一個命令行項目,新建Person類作為觸發kvc,新建Observe類作為KVO監聽器:
main.m:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Observer *observer = [[Observer alloc] init];
Person *person = [[Person alloc] init];
// 添加KVO監聽
[person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 通過KVC修改age屬性
[person setValue:@10 forKey:@"age"];
// setAge:
// 移除KVO監聽
[person removeObserver:observer forKeyPath:@"age"];
}
return 0;
}
Observer.m:
@implementation Observer
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"observeValueForKeyPath - %@", change);
}
@end
結果確實有打印出KVO方法:
image.png
從上述結果看通過KVC修改屬性值確實能調用KVO屬性監聽.上一篇我們講到KVO實際上會調用willChangeValueForKey
和didChangeValueForKey
方法,我們再驗證一下是否通過kvc修改屬性值會觸發這兩個方法:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Observer *observer = [[Observer alloc] init];
Person *person = [[Person alloc] init];
// 添加KVO監聽
[person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 通過KVC修改age屬性
[person setValue:@10 forKey:@"age"];
// 移除KVO監聽
[person removeObserver:observer forKeyPath:@"age"];
}
return 0;
}
在Person.m中添加代碼:
- (void)willChangeValueForKey:(NSString *)key
{
[super willChangeValueForKey:key];
NSLog(@"willChangeValueForKey - %@", key);
}
- (void)didChangeValueForKey:(NSString *)key
{
NSLog(@"didChangeValueForKey - begin - %@", key);
[super didChangeValueForKey:key];
NSLog(@"didChangeValueForKey - end - %@", key);
}
結果確實有打印這兩個方法
也就是說當調用
[person setValue:@20 forKeyPath:@"age"];
大概等價于下面這段代碼:
[person willChangeValueForKey:@"age"];
person->age = 20;
[person didChangeValueForKey:@"age"];
KVC的賦值和取值過程是怎樣的?原理是什么
setValue:forKey:的原理
image.png
簡單分析一下:
- 當系統執行到
[person setValue:@10 forKeyPath:@"age"];
方法的時候,系統首先會去查找setAge:
方法,如果該方法不存在則繼續去尋找_setAge:
方法,如果找到則傳遞參數調用方法. - 沒有找到 則去查看
- (BOOL) accessInstanceVariablesDirectly
方法的返回值,其默認值就是YES,系統就會按照_age
、_isAge
、age
、isAge
順序查找成員變量,如果找到了則直接賦值,否則執行步驟3; - 系統自動調用
setValue:forUndefinedKey:
方法并且拋出NSUnknownKeyException
的異常.
valueForKey:的原理也類似
image.png
總結:
到此我們就非常明白整個KVC的實現過程了,通過KVC修改屬性值同樣能觸發KVO監聽,說明KVC內部實現也是能夠支持KVO的,所以才有網上那句話“KVO是基于KVC實現的”