KVC實現(xiàn)原理

博客鏈接KVC實現(xiàn)原理

KVC全稱是Key Value Coding,定義在NSKeyValueCoding.h文件中洞斯。KVC提供了一種間接訪問其屬性方法或成員變量的機制绑咱,可以通過字符串來訪問對應(yīng)的屬性方法或成員變量。關(guān)于KVC的實現(xiàn)主要依賴于其搜索規(guī)則娇澎。

搜索規(guī)則

在賦值過程中,我們會使用- (void)setValue:(id)value forKey:(NSString *)key或者(void)setValue:(id)value forKeyPath:(NSString *)keyPath;進行KVC的賦值操作睹晒。在取值過程中趟庄,我們會使用- (id)valueForKey:(NSString *)key;或者- (id)valueForKeyPath:(NSString *)keyPath;

KVC在通過key或者keyPath進行操作的時候伪很,可以查找屬性方法戚啥、成員變量等,查找的時候可以兼容多種命名锉试。具體的查找規(guī)則在KVC官方文檔中可以找到猫十。

KVC的實現(xiàn)主要依賴于settergetter方法,所以關(guān)于命名需要符合蘋果的規(guī)范呆盖。另外在搜索過程中accessInstanceVariablesDirectly這個只讀屬性也起著重要的作用拖云。這個屬性表示是否允許讀取實例變量的值,如果為YES則在KVC查找的過程中应又,從內(nèi)存中讀取屬性實例變量的值江兢,默認為YES。

賦值原理

setValue:forKey:為例丁频,其內(nèi)部實現(xiàn)主要有以下步驟:

  1. set<Key>:杉允、_set<Key>的順序查找對應(yīng)命名的setter方法邑贴,如果找到的話,調(diào)用這個方法并將值傳進去(根據(jù)需要進行對象轉(zhuǎn)換)叔磷;

  2. 如果沒有發(fā)現(xiàn)setter方法拢驾,但是accessInstanceVariablesDirectly類屬性返回YES,則按_<key>改基、_is<Key>繁疤、<key>is<Key>的順序查找一個對應(yīng)的實例變量秕狰。如果發(fā)現(xiàn)則將value賦值給實例變量稠腊;

  3. 如果沒有發(fā)現(xiàn)setter方法或?qū)嵗兞浚瑒t調(diào)用setValue:forUndefinedKey:方法鸣哀,默認拋出一個異常架忌,但是一個NSObject的子類可以提出合適的行為。

接著我們用代碼進行相關(guān)的測試:

實驗1:驗證setter方法

// model1
@interface KVCTestModel1 : NSObject

@end

@implementation KVCTestModel1

- (void)setName:(NSString *)name {
    NSLog(@"%s", __func__);
}

- (void)_setName:(NSString *)name {
    NSLog(@"%s", __func__);
}

@end

// model2
@interface KVCTestModel2 : NSObject

@end

@implementation KVCTestModel2

- (void)_setName:(NSString *)name {
    NSLog(@"%s", __func__);
}

@end

// model3
@interface KVCTestModel3 : NSObject

@end

@implementation KVCTestModel3

@end

// 調(diào)用
- (void)_testKVC {
    KVCTestModel1 *model1 = [[KVCTestModel1 alloc] init];
    [model1 setValue:@"Nero" forKey:@"name"];
    
    KVCTestModel2 *model2 = [[KVCTestModel2 alloc] init];
    [model2 setValue:@"Nero" forKey:@"name"];
    
    KVCTestModel3 *model3 = [[KVCTestModel3 alloc] init];
    [model3 setValue:@"Nero" forKey:@"name"];
}

執(zhí)行結(jié)果如下:


kvc_setter1

實驗2:驗證實例變量

// model1
@interface KVCTestModel1 : NSObject {
    NSString *_name;
    NSString *_isName;
    NSString *name;
    NSString *isName;
}

@end

// model2
@interface KVCTestModel2 : NSObject {
    NSString *_isName;
    NSString *name;
    NSString *isName;
}

@end

// model3
@interface KVCTestModel3 : NSObject {
    NSString *name;
    NSString *isName;
}

@end

// model4
@interface KVCTestModel4 : NSObject {
    NSString *isName;
}

@end

// 調(diào)用
- (void)_testKVC {
    self.kvcTestModel1 = [[KVCTestModel1 alloc] init];
    [self.kvcTestModel1 setValue:@"Nero" forKey:@"name"];
    
    self.kvcTestModel2 = [[KVCTestModel2 alloc] init];
    [self.kvcTestModel2 setValue:@"Nero" forKey:@"name"];

    self.kvcTestModel3 = [[KVCTestModel3 alloc] init];
    [self.kvcTestModel3 setValue:@"Nero" forKey:@"name"];

    self.kvcTestModel4 = [[KVCTestModel4 alloc] init];
    [self.kvcTestModel4 setValue:@"Nero" forKey:@"name"];
}

執(zhí)行結(jié)果如下:


kvc_setter2

另外如果設(shè)置accessInstanceVariablesDirectly返回為NO我衬,即使有符合命名規(guī)范的實例變量名叹放,KVC也無法賦值成功;setValue:forUndefinedKey:默認會拋出一個異常挠羔,你可以用重寫這個方法用來攔截井仰。

賦值原理流程圖如下:


kvc_setter_process.jpg

取值原理

valueForKey:為例,其內(nèi)部實現(xiàn)主要有以下幾步:

  1. 通過getter方法搜索實例破加,以get<Key>, <key>, is<Key>, _<key>的順序搜索符合規(guī)則的方法俱恶,如果有,就調(diào)用對應(yīng)的方法范舀;

  2. 如果沒有發(fā)現(xiàn)簡單getter方法速那,并且在類方法accessInstanceVariablesDirectly是返回YES的的情況下搜索一個名為_<key>_is<Key>尿背、<key>端仰、is<Key>的實例;

  3. 如果返回值是一個對象指針田藐,則直接返回這個結(jié)果荔烧;如果返回值是一個基礎(chǔ)數(shù)據(jù)類型,但是這個基礎(chǔ)數(shù)據(jù)類型是被NSNumber支持的汽久,則存儲為NSNumber并返回鹤竭;如果返回值是一個不支持NSNumber的基礎(chǔ)數(shù)據(jù)類型,則通過NSValue進行存儲并返回景醇;

  4. 在上述情況都失敗的情況下調(diào)用valueForUndefinedKey:方法臀稚,默認拋出異常,但是子類可以重寫此方法三痰。

由于和前面的賦值原理實驗相似吧寺,這里就不添加相關(guān)的驗證代碼了窜管。另外valueForKey:返回的結(jié)果還可能是數(shù)組或者其他集合類型,所以在上面第1步和第2步之間還有一些其他的規(guī)則稚机,這些規(guī)則用來判斷是否是數(shù)組或者其他集合類型的規(guī)則幕帆,但是我覺得忽略這些規(guī)則跟整體流程理解沖突不大,所以就忽略掉了(具體的在官在KVC官方文檔中可以找到赖条。)失乾。

補充:

面試題分析: KVC能否能夠觸發(fā)KVO

答案是肯定的。

測試代碼如下:

@interface KVCTestModel1 : NSObject {
    @public
    NSString *_name;
}

@end

@implementation KVCTestModel1

@end

// 測試代碼
KVCTestModel1 *model1 = [[KVCTestModel1 alloc] init];
[model1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

// 直接修改成員變量
model1 -> _name = @"Nero1";
NSLog(@"%@", [model1 valueForKey:@"name"]);

// 手動觸發(fā)KVO
[model1 willChangeValueForKey:@"name"];
model1 -> _name = @"Nero2";
[model1 didChangeValueForKey:@"name"];
NSLog(@"%@", [model1 valueForKey:@"name"]);

// KVC賦值
[model1 setValue:@"Nero3" forKeyPath:@"name"];
NSLog(@"%@", [model1 valueForKey:@"name"]);

[model1 removeObserver:self forKeyPath:@"name"];

打印結(jié)果:


image

通過上面的代碼我們可以認為纬乍,在以KVC的方式對變量進行賦值的時候碱茁,會判斷該對象是否使用了KVO,如果是仿贬,則會觸發(fā)KVO纽竣。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市诅蝶,隨后出現(xiàn)的幾起案子退个,更是在濱河造成了極大的恐慌募壕,老刑警劉巖调炬,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舱馅,居然都是意外死亡缰泡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門代嗤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棘钞,“玉大人,你說我怎么就攤上這事干毅∫瞬拢” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵硝逢,是天一觀的道長姨拥。 經(jīng)常有香客問我,道長渠鸽,這世上最難降的妖魔是什么叫乌? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮徽缚,結(jié)果婚禮上憨奸,老公的妹妹穿的比我還像新娘。我一直安慰自己凿试,他們只是感情好排宰,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布似芝。 她就那樣靜靜地躺著,像睡著了一般额各。 火紅的嫁衣襯著肌膚如雪国觉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天虾啦,我揣著相機與錄音麻诀,去河邊找鬼。 笑死傲醉,一個胖子當(dāng)著我的面吹牛蝇闭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硬毕,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呻引,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吐咳?” 一聲冷哼從身側(cè)響起逻悠,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎韭脊,沒想到半個月后童谒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡沪羔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年饥伊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔫饰。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡琅豆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篓吁,到底是詐尸還是另有隱情茫因,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布杖剪,位于F島的核電站冻押,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏摘盆。R本人自食惡果不足惜翼雀,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望孩擂。 院中可真熱鬧狼渊,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至米苹,卻和暖如春糕伐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蘸嘶。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工良瞧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人训唱。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓褥蚯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親况增。 傳聞我的和親對象是個殘疾皇子赞庶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

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

  • KVC(Key-value coding)鍵值編碼,單看這個名字可能不太好理解澳骤。其實翻譯一下就很簡單了歧强,就是指iO...
    我的夢工廠閱讀 891評論 1 8
  • KVC(Key-valuecoding)鍵值編碼,單看這個名字可能不太好理解为肮。其實翻譯一下就很簡單了摊册,就是指iOS...
    榕樹頭閱讀 713評論 0 2
  • 一、什么是KVC弥锄? KVC是Key Value Coding鍵值編碼丧靡,是一種通過字符串的名字(Key)來訪問類屬性...
    Coder_LRT閱讀 414評論 0 0
  • 一蟆沫、什么是KVC籽暇? KVC是Key Value Coding鍵值編碼,是一種通過字符串的名字(Key)來訪問類屬性...
    DeveloperBlock閱讀 471評論 0 0
  • KVC簡單介紹 KVC(Key-value coding)鍵值編碼饭庞,就是指iOS的開發(fā)中戒悠,可以允許開發(fā)者通過Key...
    公子無禮閱讀 1,394評論 0 6