探索iOS底層原理第三篇——KVC

本系列是學習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實際上會調用willChangeValueForKeydidChangeValueForKey方法,我們再驗證一下是否通過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

簡單分析一下:

  1. 當系統執行到[person setValue:@10 forKeyPath:@"age"];方法的時候,系統首先會去查找setAge:方法,如果該方法不存在則繼續去尋找_setAge:方法,如果找到則傳遞參數調用方法.
  2. 沒有找到 則去查看- (BOOL) accessInstanceVariablesDirectly方法的返回值,其默認值就是YES,系統就會按照_age_isAgeageisAge順序查找成員變量,如果找到了則直接賦值,否則執行步驟3;
  3. 系統自動調用setValue:forUndefinedKey:方法并且拋出NSUnknownKeyException的異常.

valueForKey:的原理也類似

image.png

總結:

到此我們就非常明白整個KVC的實現過程了,通過KVC修改屬性值同樣能觸發KVO監聽,說明KVC內部實現也是能夠支持KVO的,所以才有網上那句話“KVO是基于KVC實現的”

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

推薦閱讀更多精彩內容