KVC 的原理分析

定義

KVC 是 Key Value Coding 的簡稱龙屉,鍵值對編碼,遵循 NSKeyValueCoding 協(xié)議满俗,可以像操作字典一樣操作一個對象转捕,通過 key 來直接取值和賦值的機制,而不是通過調(diào)用 setter唆垃、getter 方法訪問五芝。

相關(guān)API

由下圖 NSKeyValueCoding.h 頭文件中,我們可以看到一些相關(guān)的 API辕万。

1.png

其中枢步,以下四個是我們較為常用的 API

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key; 

API 區(qū)別

這里簡單說一下 forKey 以及 forKeyPath 的區(qū)別。

@interface Student : NSObject
@property (nonatomic, assign) int age;
@end

@interface Teacher : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) Student *student;
@end

如上述代碼所示渐尿,一個 Teacher 類中醉途,包含一個 name 的屬性和一個 student的對象,Student 對象中包含 age 的屬性砖茸,那么隘擎,如下代碼所示可以看出,對于 Teacher 類中的 name 屬性使用 KVC 方法凉夯,無論是使用 setValue: forKey: 或是 setValue: forKeyPath: 都是可以實現(xiàn)的货葬。但是采幌,如果是對 student 中的 age,則必須使用 setValue: forKeyPath:方法震桶。
所以休傍,我們不難看出,key 是只能訪問當(dāng)前對象的屬性尼夺,如果想要層層向下訪問的話尊残,就需要使用 keyPath

    self.teacher = [[Teacher alloc] init];
    self.teacher.name = @"老明";
    
    // 這里如果想要對 student 中的屬性進行賦值淤堵,那么必須先對其進行實例化
    self.teacher.student = [[Student alloc] init];
    self.teacher.student.age = 10;
    [self.teacher setValue:@"老李" forKey:@"name"];
    NSLog(@"teacherName1 = %@",self.teacher.name);
    NSLog(@"studentAge1 = %d",self.teacher.student.age);
    
    [self.teacher setValue:@"老劉" forKeyPath:@"name"];
    [self.teacher setValue:@30 forKeyPath:@"student.age"];
    NSLog(@"teacherName2 = %@",self.teacher.name);
    NSLog(@"studentAge2 = %d",self.teacher.student.age);

    log打印出來結(jié)果如下:
    teacherName1 = 老李
    studentAge1 = 10
    teacherName2 = 老劉
    studentAge2 = 30

是否觸發(fā) KVO

對 teacher 的 name 屬性執(zhí)行監(jiān)聽寝衫,查看其回調(diào)方法observeValueForKeyPath:ofObject:change:context:是否執(zhí)行

    // 1. 添加 KVO 監(jiān)聽
    [self.teacher addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    // 2. 通過 KVC 修改 name 屬性值
    [self.teacher setValue:@"teacherNew" forKey:@"name"];
    // 3. 移除監(jiān)聽
    [self.teacher removeObserver:self forKeyPath:@"name"];

log 打印結(jié)果如下:
 object: <Teacher: 0x600001f1ddc0>
keyPath: name
change: {
    kind = 1;
    new = teacherNew;
    old = "\U8001\U5218";
}

從上述打印結(jié)果不難看出,KVC 修改屬性會觸發(fā) KVO拐邪。

setValue:forKey: 原理

  1. setValue:forKey: 方法在調(diào)用時慰毅,首先會去調(diào)用 setKey: 方法,如果找不到方法扎阶,則會查找調(diào)用 _setKey: 的方法汹胃,如果找到方法,那么直接傳遞參數(shù)調(diào)用方法东臀,如果兩個方法均找不到着饥,那么調(diào)用 accessInstanceVariablesDirectly
    其中 accessInstanceVariablesDirectly(是否能直接訪問成員變量) 方法的默認(rèn)返回值是YES惰赋。
  2. accessInstanceVariablesDirectly 返回 NO宰掉,則拋出異常。
  3. accessInstanceVariablesDirectly 返回 YES赁濒,則按順序 _key轨奄、_isKey、key拒炎、isKey 依次往后的順序去查找成員變量挪拟,如果找到成員變量,則直接賦值击你,找不到則拋出異常玉组。

具體方法調(diào)用步驟,可參照下圖所示流程:

setValue:forKey:

如果所示步驟丁侄,可通過依次代碼設(shè)置 setKey 以及 _setKey: 方法來進行驗證球切。

valueForKey: 原理

通過 setValue:forKey: 方法,不難得出 valueForKey: 的執(zhí)行順序绒障。

  1. 按順序 getKey吨凑、key、 isKey、_key 依次往后的順序去調(diào)用取值鸵钝,如果找到方法糙臼,則直接調(diào)用方法。
  2. accessInstanceVariablesDirectly 返回 NO恩商,則拋出異常变逃。
  3. accessInstanceVariablesDirectly 返回 YES,則按順序 _key怠堪、_isKey揽乱、key、isKey 依次往后的順序去查找成員變量粟矿,如果找到成員變量凰棉,則直接取值,找不到則拋出異常陌粹。
    valueForKey:
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撒犀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子掏秩,更是在濱河造成了極大的恐慌或舞,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒙幻,死亡現(xiàn)場離奇詭異映凳,居然都是意外死亡,警方通過查閱死者的電腦和手機邮破,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門诈豌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人决乎,你說我怎么就攤上這事∨勺” “怎么了构诚?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铆惑。 經(jīng)常有香客問我范嘱,道長,這世上最難降的妖魔是什么员魏? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任丑蛤,我火速辦了婚禮,結(jié)果婚禮上撕阎,老公的妹妹穿的比我還像新娘受裹。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布棉饶。 她就那樣靜靜地躺著厦章,像睡著了一般。 火紅的嫁衣襯著肌膚如雪照藻。 梳的紋絲不亂的頭發(fā)上袜啃,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音幸缕,去河邊找鬼群发。 笑死,一個胖子當(dāng)著我的面吹牛发乔,可吹牛的內(nèi)容都是我干的熟妓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼列疗,長吁一口氣:“原來是場噩夢啊……” “哼滑蚯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抵栈,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤告材,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后古劲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斥赋,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年产艾,在試婚紗的時候發(fā)現(xiàn)自己被綠了疤剑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡闷堡,死狀恐怖隘膘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杠览,我是刑警寧澤弯菊,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站踱阿,受9級特大地震影響管钳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜软舌,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一才漆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧佛点,春花似錦醇滥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雌续,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間书妻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工躬拢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躲履,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓聊闯,卻偏偏與公主長得像工猜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子菱蔬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • KVC(Key-value coding)鍵值編碼篷帅,iOS的開發(fā)中,可以允許開發(fā)者通過Key名直接訪問對象的屬性拴泌,...
    CALayer_Sai閱讀 2,515評論 0 4
  • KVC是Key Value Coding的簡稱魏身。它是一種可以通過字符串的名字(key)來訪問類屬性的機制。而不是通...
    153037c65b0c閱讀 11,459評論 15 17
  • KVC(Key-value coding)鍵值編碼蚪腐,單看這個名字可能不太好理解箭昵。其實翻譯一下就很簡單了,就是指iO...
    我的夢工廠閱讀 891評論 1 8
  • KVC(Key-valuecoding)鍵值編碼回季,單看這個名字可能不太好理解家制。其實翻譯一下就很簡單了,就是指iOS...
    榕樹頭閱讀 702評論 0 2
  • KVC是Key Value Coding的簡稱泡一。它是一種可以通過字符串的名字(key)來訪問類屬性的機制颤殴。而不是通...
    _李恒閱讀 741評論 0 0