iOS--KVC

Key-Value Coding Programming Guide

Key-value coding是由NSKeyValueCoding非正式協(xié)議啟用的一種機(jī)制夕土,對(duì)象采用該協(xié)議來提供對(duì)其屬性的間接訪問齐帚。當(dāng)對(duì)象符合KVC時(shí)弟灼,可以通過簡(jiǎn)潔、統(tǒng)一的消息傳遞接口通過字符串參數(shù)對(duì)其屬性進(jìn)行尋址藕畔。這種間接訪問機(jī)制補(bǔ)充了實(shí)例變量及其關(guān)聯(lián)訪問器方法提供的直接訪問。

通常使用getter方法來訪問對(duì)象的屬性。getter返回屬性的值吼旧。setter設(shè)置屬性的值梨与。在Objective-C中堕花,還可以直接訪問屬性的底層實(shí)例變量。遵循KVC的對(duì)象提供了一個(gè)簡(jiǎn)單的消息傳遞接口粥鞋,該接口與其所有屬性保持一致缘挽。

KVC是許多其他Cocoa技術(shù)的基礎(chǔ)概念,例如key-value observing陷虎、Cocoa bindings到踏、Core Data和AppleScript-ability。在某些情況下尚猿,還可以幫助簡(jiǎn)化代碼窝稿。

使用符合 Key-Value Coding 的對(duì)象

對(duì)象從NSObject(直接或間接)繼承時(shí)通常采用KVC,NSObject既采用NSKeyValueCoding協(xié)議凿掂,又為基本方法提供了默認(rèn)實(shí)現(xiàn)伴榔。這樣的對(duì)象允許其他對(duì)象通過一個(gè)緊湊的消息傳遞接口來完成以下操作:

  • 訪問對(duì)象的屬性。協(xié)議指定方法庄萎,例如通用getter valueForKey:和通用setter setValue:forKey:踪少,用于通過對(duì)象的名稱或鍵(參數(shù)化為字符串)訪問對(duì)象屬性。這些相關(guān)方法的默認(rèn)實(shí)現(xiàn)使用鍵來定位底層數(shù)據(jù)并與之交互糠涛, 如Accessing Object Properties援奢。

  • 操作集合屬性。操作集合屬性的訪問方法的默認(rèn)實(shí)現(xiàn)與對(duì)象的集合屬性(如NSArray對(duì)象)一起工作忍捡,就像任何其他屬性一樣集漾。此外切黔,如果對(duì)象為屬性定義了集合訪問器方法,則它支持對(duì)集合內(nèi)容的key-value訪問具篇。這通常比直接訪問更有效纬霞,并允許通過標(biāo)準(zhǔn)化接口使用自定義集合對(duì)象,如 Accessing Collection Properties驱显。

  • 在集合對(duì)象上調(diào)用集合操作符诗芜。當(dāng)訪問符合KVC的對(duì)象中的集合屬性時(shí),可以向key字符串中插入集合操作符埃疫,如Using Collection Operators伏恐。集合操作符指示默認(rèn)的NSKeyValueCoding getter實(shí)現(xiàn)對(duì)集合進(jìn)行操作,然后返回一個(gè)新的栓霜、過濾過的集合版本脐湾,或者返回一個(gè)表示集合某些特征的單個(gè)值。

  • 訪問非屬性叙淌。協(xié)議的默認(rèn)實(shí)現(xiàn)檢測(cè)非對(duì)象屬性秤掌,包括標(biāo)量和結(jié)構(gòu),并自動(dòng)將它們包裝和展開為對(duì)象鹰霍,以便在協(xié)議接口上使用闻鉴,如在Representing Non-Object Values。此外茂洒,該協(xié)議聲明了一個(gè)方法孟岛,允許兼容的對(duì)象在通過KVC接口在非對(duì)象屬性上設(shè)置nil值時(shí)提供合適的操作。

  • 通過key path訪問屬性督勺。當(dāng)有一個(gè)遵循KVC的層次結(jié)構(gòu)對(duì)象時(shí)渠羞,可以使用key path的方法調(diào)用來向下挖掘,使用單個(gè)調(diào)用在層次結(jié)構(gòu)內(nèi)部獲取或設(shè)置一個(gè)深層次的值智哀。

對(duì)對(duì)象使用 Key-Value Coding

為了使自己的對(duì)象KVC兼容次询,要確保采用NSKeyValueCoding非正式協(xié)議并實(shí)現(xiàn)相應(yīng)的方法,如作為通用getter的valueForKey:和作為通用setter的setValue:forKey:瓷叫。NSObject采用了這個(gè)協(xié)議屯吊,并為這些方法和其他基本方法提供了默認(rèn)實(shí)現(xiàn)。因此摹菠,如果從NSObject派生對(duì)象(或其眾多子類中的任何一個(gè))盒卸,那么以上大部分工作已經(jīng)完成了。

為了讓默認(rèn)方法執(zhí)行它們的工作次氨,要確保對(duì)象的訪問器方法和實(shí)例變量遵守某些定義良好的模式蔽介。這允許默認(rèn)實(shí)現(xiàn)在響應(yīng)KVC的消息時(shí)找到對(duì)象的屬性。然后,通過提供驗(yàn)證和處理某些特殊情況的方法虹蓄,可以選擇擴(kuò)展和定制谷朝。

其他依賴于KVC的Cocoa技術(shù)

符合的對(duì)象可以應(yīng)用于各種依賴于這種訪問的Cocoa技術(shù),包括:

  • Key-value observing武花。鍵-值觀察該機(jī)制允許對(duì)象注冊(cè)由另一個(gè)對(duì)象的屬性更改驅(qū)動(dòng)的異步通知,如Key-Value Observing Programming Guide中所述杈帐。

  • Cocoa bindings体箕。 這些技術(shù)完全實(shí)現(xiàn)了Model-View-Controller 范例,其中models封裝應(yīng)用程序數(shù)據(jù)挑童,views顯示和編輯數(shù)據(jù)累铅,controllers在兩者之間進(jìn)行協(xié)調(diào)。了解更多關(guān)于Cocoa綁定的信息參閱 *Cocoa Bindings Programming Topics站叼。

  • Core Data娃兽。 該框架為與對(duì)象生命周期和對(duì)象圖管理(包括持久性)相關(guān)的常見任務(wù)提供了通用的和自動(dòng)化的解決方案。

訪問對(duì)象屬性

對(duì)象通常在其@interface聲明中指定屬性尽楔,這些屬性有以下幾種類別:

  • Attributes投储。屬性,這些是簡(jiǎn)單的值阔馋,例如scalars玛荞、strings或Boolean值。值對(duì)象(如NSNumber)和其他不可變類型(如NSColor)也被視為屬性呕寝。

  • To-one relationships一對(duì)一的關(guān)系勋眯。這些是可變的對(duì)象,具有自己的屬性下梢。對(duì)象的屬性可以在對(duì)象本身不發(fā)生變化的情況下更改客蹋。例如,銀行帳戶對(duì)象可能有一個(gè)owner屬性孽江,該屬性是Person對(duì)象的實(shí)例讶坯,而Person對(duì)象本身具有address屬性。owner的address可能會(huì)改變岗屏,而銀行帳戶的所有者owner沒有改變闽巩。改變的只有所有者的地址。

  • To-many relationships 一對(duì)很多關(guān)系担汤。這些是集合對(duì)象涎跨。通常使用NSArray或NSSet的實(shí)例來保存這樣的集合,也可以使用自定義集合類崭歧。

清單2-1中聲明的BankAccount對(duì)象演示了每種類型的屬性隅很。

@interface LMBack : NSObject
@property(nonatomic)NSNumber *currentBalance;
@property(nonatomic)LMPerson *person;
@property(nonatomic)NSArray *records;
@end

為了保持封裝,對(duì)象通常為屬性提供訪問器方法∈逵可以顯式地編寫這些方法屋彪,也可以依賴編譯器自動(dòng)合成它們。不管怎樣绒尊,使用這些訪問器之一的代碼的作者必須在編譯代碼之前將屬性名寫入代碼畜挥。訪問器方法的名稱將成為使用它的代碼的靜態(tài)部分。例如婴谱,給定清單2-1中聲明的bank account對(duì)象蟹但,編譯器將合成一個(gè)setter,可以為myAccount實(shí)例調(diào)用它:
[myAccount setCurrentBalance:@(100.0)]谭羔;

這是直接調(diào)用华糖,但缺乏靈活性。與key-value coding 兼容的對(duì)象提供了一種更通用的機(jī)制瘟裸,可以使用字符串標(biāo)識(shí)符訪問對(duì)象的屬性客叉。

用 Keys 和 Key Paths 標(biāo)識(shí)對(duì)象的屬性

鍵是標(biāo)識(shí)特定屬性的字符串。通常话告,表示屬性的鍵是在代碼中顯示的屬性本身的名稱兼搏。鍵必須使用ASCII編碼,不能包含空格沙郭,并且通常以小寫字母開頭(盡管也有例外向族,如在許多類中的URL屬性)。

因?yàn)榍鍐?-1中的BankAccount類是KVC兼容的棠绘,所以它識(shí)別鍵owner件相、currentBalance和transactions,它們是其屬性的名稱氧苍∫勾#可以通過它的鍵設(shè)置值:

[myAccount setValue:@(100.0)forKey:@“currentBalance”];

可以使用相同的方法让虐,使用不同的鍵來設(shè)置myAccount對(duì)象的所有屬性紊撕。因?yàn)閰?shù)是字符串,所以它可以在run-time時(shí)操作的變量赡突。

key-path是一系列點(diǎn)分隔鍵对扶,用于指定要遍歷的對(duì)象屬性序列。序列中第一個(gè)鍵的屬性是相對(duì)于接收器的惭缰,每個(gè)后續(xù)鍵都是相對(duì)于前一個(gè)屬性的值來計(jì)算的浪南。key-path對(duì)于通過單個(gè)方法調(diào)用深入到對(duì)象層次結(jié)構(gòu)非常有用。

例如漱受,owner.address.street應(yīng)用于銀行帳戶實(shí)例是指存儲(chǔ)在銀行帳戶所有者owner地址address中的街道street字符串的值络凿,假設(shè)Person和address類也符合鍵值編碼。

[myAccount valueForKeyPath:@"owner.address.street"]);

注意:在Swift中,您可以使用#keyPath表達(dá)式絮记,而不是使用字符串來指示鍵或鍵路徑摔踱。這提供了編譯時(shí)檢查的優(yōu)勢(shì),如使用Swift with Cocoa和Objective-C(Swift 3)指南中的Keys and Key Paths部分所述怨愤。

使用 Keys 獲取屬性值

當(dāng)對(duì)象采用NSKeyValueCoding協(xié)議時(shí)派敷,它是NSKeyValueCoding協(xié)議的。從NSObject繼承的對(duì)象提供了協(xié)議基本方法的默認(rèn)實(shí)現(xiàn)撰洗,它會(huì)自動(dòng)采用具有某些默認(rèn)行為的協(xié)議篮愉。這樣的對(duì)象至少實(shí)現(xiàn)了以下基本的基于鍵的getter:

  • valueForKey:-返回由key參數(shù)命名的屬性的值。如果根據(jù)Accessor Search Patterns中描述的規(guī)則了赵,無法找到由鍵命名的屬性,則對(duì)象將向自己發(fā)送valueForUndefinedKey:信息甸赃。valueForUndefinedKey:的默認(rèn)實(shí)現(xiàn)引發(fā)了一個(gè)NSUndefinedKeyException柿汛,但子類可能會(huì)覆蓋此行為并更優(yōu)雅地處理這種情況。

  • valueForKeyPath:-返回指定key-path對(duì)于接收器的值埠对。key-path序列中的任何對(duì)象络断,如果不符合特定鍵的鍵值編碼,valueForKey:的默認(rèn)實(shí)現(xiàn)找不到訪問器方法项玛,則會(huì)收到valueForUndefinedKey:消息貌笨。

  • dictionaryWithValuesForKeys:—返回與接收器相關(guān)的鍵數(shù)組的值。該方法為數(shù)組中的每個(gè)鍵調(diào)用valueForKey:襟沮。返回的NSDictionary包含數(shù)組中所有鍵的值锥惋。

注意:集合對(duì)象(如NSArray、NSSet和NSDictionary)不能包含nil值开伏。使用NSNull對(duì)象表示零值膀跌。NSNull提供一個(gè)表示對(duì)象屬性的nil值的實(shí)例。dictionaryWithValuesForKeys:和相關(guān)的setValuesForKeysWithDictionary:的默認(rèn)實(shí)現(xiàn)在NSNull(在dictionary參數(shù)中)和nil(在存儲(chǔ)的屬性中)之間自動(dòng)轉(zhuǎn)換固灵。

使用鍵路徑尋址屬性時(shí)捅伤,如果鍵路徑中的最后一個(gè)鍵是對(duì)多關(guān)系(即,它引用集合)巫玻,則返回的值是一個(gè)集合丛忆,其中包含對(duì)多鍵右側(cè)鍵的所有值。例如仍秤,請(qǐng)求key path的records.balance值返回一個(gè)數(shù)組蛙吏,該數(shù)組包含所有事務(wù)的所有款項(xiàng)金額。這也適用于key path中的多個(gè)數(shù)組蒜田。key pathaccounts.records.payee返回一個(gè)包含所有帳戶中所有交易的所有受款人對(duì)象的數(shù)組兵志。

for (NSInteger i = 0; i < 3; i++) {
        Transaction *t = [[Transaction alloc] init];
        LMPerson *p = [[LMPerson alloc] init];
        p.name = [NSString stringWithFormat:@"%@-啊哈哈", @(i)];
        t.person = p;
        t.balance = @(i*100);
        [arr addObject:t];
}
back.records = [arr copy];
NSLog(@"%@", [back valueForKeyPath:@"records.balance"]);

2020-08-04 17:23:42.629599+0800 Test[57233:8661545] (
    0,
    100,
    200
)

使用 Key 設(shè)置屬性值

與getter一樣,基于NSObject中找到的NSKeyValueCoding協(xié)議的實(shí)現(xiàn),符合KVC的對(duì)象也提供了一組通用setter:

  • setValue:forKey::-將相對(duì)于接收消息的對(duì)象的指定鍵的值設(shè)置為給定值导坟。setValue:forKey:的默認(rèn)展開表示標(biāo)量和結(jié)構(gòu)的NSNumber和NSValue對(duì)象屿良,并將它們指定給屬性。有關(guān)包裝和展開語義的詳細(xì)信息惫周,請(qǐng)參見 Representing Non-Object Values 尘惧。
    如果指定的鍵對(duì)應(yīng)于接收setter調(diào)用的對(duì)象不具有的屬性,則該對(duì)象向自身發(fā)送一個(gè)setValue:forUndefinedKey:消息递递。setValue:forUndefinedKey:引發(fā)NSUndefinedKeyException喷橙。但是,子類可以重寫此方法以自定義方式處理請(qǐng)求登舞。

  • setValue:forKeyPath:—設(shè)置相對(duì)于接收器的指定key path路徑處的給定值贰逾。在一個(gè)特定的密鑰序列中,任何一個(gè)key path序列中的key-value 都是不兼容的setValue:forUndefinedKey:消息菠秒。

  • setValuesForKeysWithDictionary:-使用指定字典中的值設(shè)置接收器的屬性疙剑,使用字典鍵標(biāo)識(shí)屬性。默認(rèn)實(shí)現(xiàn)對(duì)每個(gè)key-value pair調(diào)用setValue:forKey:践叠,根據(jù)需要用nil替換NSNull對(duì)象言缤。

在默認(rèn)實(shí)現(xiàn)中,當(dāng)嘗試將一個(gè)非對(duì)象屬性設(shè)置為nil值時(shí)禁灼,遵循key-value coding 的對(duì)象向自己發(fā)送一個(gè)setNilValueForKey:消息管挟。setNilValueForKey:會(huì)引發(fā)一個(gè)NSInvalidArgumentException異常,但可以重寫這個(gè)行為來代替默認(rèn)值或標(biāo)記值弄捕,如處理Handling Non-Object Values僻孝。

Using Keys to Simplify Object Access --使用鍵簡(jiǎn)化對(duì)象訪問

要了解基于鍵的getter和setter如何簡(jiǎn)化代碼,請(qǐng)參考以下示例守谓。在macOS中皮璧,NSTableViewNSOutlineView對(duì)象將一個(gè)標(biāo)識(shí)符字符串與它們的每一列關(guān)聯(lián)起來。如果支持表的模型對(duì)象不符合KVC分飞,表的數(shù)據(jù)源方法將被迫依次檢查每個(gè)列標(biāo)識(shí)符悴务,以找到要返回的正確屬性,如清單2-2所示譬猫。而且讯檐,將來當(dāng)向模型中添加另一個(gè)屬性時(shí),在本例中是' Person '對(duì)象染服,還必須重新添加訪問數(shù)據(jù)源方法别洪,添加另一個(gè)條件來測(cè)試新屬性并返回相關(guān)值
清單2-2沒有鍵值編碼的數(shù)據(jù)源方法的實(shí)現(xiàn)

- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row
{
    id result = nil;
    Person *person = [self.people objectAtIndex:row];
 
    if ([[column identifier] isEqualToString:@"name"]) {
        result = [person name];
    } else if ([[column identifier] isEqualToString:@"age"]) {
        result = @([person age]);  // Wrap age, a scalar, as an NSNumber
    } else if ([[column identifier] isEqualToString:@"favoriteColor"]) {
        result = [person favoriteColor];
    } // And so on...
 
    return result;
}

利用key-value coding 兼容的' Person '對(duì)象,以列標(biāo)識(shí)符作為key柳刮,使用valueForKey:的getter數(shù)據(jù)源方法返回適當(dāng)?shù)闹低诙狻T谝院筇砑有铝袝r(shí)痒钝,只要列標(biāo)識(shí)符始終與模型對(duì)象的屬性名稱匹配,就會(huì)繼續(xù)工作痢毒。

- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row
{
    return [[self.people objectAtIndex:row] valueForKey:[column identifier]];
}

訪問屬性集合

符合鍵-值編碼的對(duì)象以與其他屬性相同的方式公開它們的多個(gè)屬性送矩。你可以獲取或設(shè)置一個(gè)集合對(duì)象,就像使用valueForKey:和setValue:forKey:(或它們的鍵路徑等效物)獲取或設(shè)置任何其他對(duì)象一樣哪替。但是栋荸,當(dāng)希望操作這些集合的內(nèi)容時(shí),通常最有效的方法是使用協(xié)議定義的可變代理方法凭舶。

該協(xié)議為集合對(duì)象訪問定義了三種不同的代理方法晌块,每一種都有一個(gè)鍵和一個(gè)鍵路徑變體:

當(dāng)您對(duì)代理對(duì)象進(jìn)行操作、向其中添加對(duì)象闽铐、從其中刪除對(duì)象或替換對(duì)象時(shí)蝶怔,協(xié)議的默認(rèn)實(shí)現(xiàn)將相應(yīng)地修改底層屬性奶浦。這比使用valueForKey:獲得一個(gè)非可變的集合對(duì)象兄墅,創(chuàng)建一個(gè)修改過的對(duì)象并修改內(nèi)容,然后使用setValue:forKey: 消息將其存儲(chǔ)回該對(duì)象更有效澳叉。在許多情況下隙咸,它比直接處理可變屬性更有效。這些方法提供了維護(hù)集合對(duì)象中持有的對(duì)象的鍵值觀察遵從性的額外好處(詳細(xì)信息請(qǐng)參閱 Key-Value Observing Programming Guide)成洗。

訪問器搜索模式

NSObject提供的NSKeyValueCoding協(xié)議的默認(rèn)實(shí)現(xiàn)使用一組明確定義的規(guī)則將key-based accessor的訪問器調(diào)用映射到對(duì)象的底層屬性五督。這些協(xié)議方法使用一個(gè)key參數(shù)來搜索它們自己的對(duì)象實(shí)例,以查找訪問器accessors瓶殃、實(shí)例變量和遵循特定命名約定的相關(guān)方法充包。理解這種默認(rèn)搜索是如何工作的還是很有幫助的,這有助于跟蹤key-value編碼的對(duì)象的行為遥椿,也有助于使您自己的對(duì)象兼容基矮。

請(qǐng)注意 :本節(jié)中的描述使用<key>或作為key-value coding協(xié)議方法中作為參數(shù)出現(xiàn)的<Key>字符串的占位符,然后該方法將其用作輔助方法調(diào)用或變量名稱查找的一部分冠场。映射的屬性名稱遵循占位符的大小寫家浇。例如,對(duì)于getters <key>is< Key>碴裙,名為hidden的屬性映射到hidden和isHidden钢悲。

Getter的搜索模式

valueForKey:的默認(rèn)實(shí)現(xiàn)点额,給定一個(gè) key參數(shù)作為輸入,執(zhí)行以下過程莺琳,從接收valueForKey:調(diào)用的類實(shí)例內(nèi)部進(jìn)行操作还棱。

  1. 在實(shí)例中搜索找到的第一個(gè)訪問器方法,其名稱如下 get<Key>, <key>, is<Key>或者 _<key>芦昔。如果找到了诱贿,就調(diào)用它,并在步驟5中處理結(jié)果咕缎。否則繼續(xù)下一步珠十。

  2. 如果沒有找到簡(jiǎn)單的訪問器方法,則在實(shí)例中搜索名稱與模式countOf<Key>objectIn<Key>AtIndex:(對(duì)應(yīng)于NSArray 類定義的基本方法)和<key>AtIndexes:(對(duì)應(yīng)于NSArray的方法objectsAtIndexes:)匹配的方法凭豪。
    如果找到第一個(gè)和至少兩個(gè)中的一個(gè)焙蹭,則創(chuàng)建一個(gè)集合代理對(duì)象,該對(duì)象響應(yīng)所有NSArray方法并返回該方法嫂伞。否則孔厉,繼續(xù)執(zhí)行步驟3。
    代理對(duì)象隨后將接收到的任何NSArray消息轉(zhuǎn)換為countOf<Key>, objectIn<Key>AtIndex:, 和 <key>AtIndexes: 的一些組合帖努,這些組合將消息發(fā)送給創(chuàng)建它的KVC對(duì)象撰豺。如果原始對(duì)象還實(shí)現(xiàn)了一個(gè)名為get<Key>:range:的可選方法,代理對(duì)象也會(huì)在適當(dāng)?shù)臅r(shí)候使用它拼余。實(shí)際上污桦,代理對(duì)象與和KVC兼容的對(duì)象一起工作,允許底層屬性像NSArray一樣工作匙监,即使它不是NSArray凡橱。

  3. 如果沒有找到簡(jiǎn)單的訪問方法或數(shù)組訪問方法組,查找三個(gè)方法亭姥,分別為 countOf<Key>稼钩, enumeratorOf<Key>memberOf<Key>: (對(duì)應(yīng)于NSSet類定義的原語方法)达罗。
    如果這三個(gè)方法都找到了坝撑,創(chuàng)建一個(gè)集合代理對(duì)象,它響應(yīng)所有NSSet方法并返回那個(gè)粮揉。否則巡李,繼續(xù)執(zhí)行步驟4。
    這個(gè)代理對(duì)象隨后將它接收到的所有NSSet消息轉(zhuǎn)換為countOf<Key>, enumeratorOf<Key>, memberOf<Key>:消息的組合滔蝉,并發(fā)送給創(chuàng)建它的對(duì)象击儡。實(shí)際上,代理對(duì)象與遵循KVC的對(duì)象一起工作蝠引,允許底層屬性像NSSet一樣運(yùn)行阳谍,即使它不是NSSet蛀柴。

  4. 如果沒有找到簡(jiǎn)單的訪問方法或集合訪問方法組,并且如果接收方的類方法accessInstanceVariablesDirectly直接返回YES矫夯,那么按照順序搜索一個(gè)實(shí)例變量:_<key>, _is<Key>, <key>, 或 is<Key>, 鸽疾。如果找到,直接獲取實(shí)例變量的值并繼續(xù)執(zhí)行步驟5训貌。否則制肮,繼續(xù)執(zhí)行步驟6。

  5. 如果檢索到的屬性值是一個(gè)對(duì)象指針递沪,只需返回結(jié)果豺鼻。
    如果值是 NSNumber支持的類型,將其存儲(chǔ)在 NSNumber實(shí)例中并返回款慨。
    如果結(jié)果是NSNumber不支持的類型儒飒,轉(zhuǎn)換為NSValue對(duì)象并返回它。

  6. 如果所有其他方法都失敗檩奠,則調(diào)用valueForUndefinedKey:桩了。這在默認(rèn)情況下會(huì)引發(fā)一個(gè)NSUndefinedKeyException異常,但是NSObject的一個(gè)子類可能提供key-specific的行為埠戳。

Setter的搜索模式

setValue:forKey:的默認(rèn)實(shí)現(xiàn)井誉,給定 keyvalue參數(shù)作為輸入,嘗試在接收調(diào)用的對(duì)象內(nèi)部設(shè)置一個(gè)名為key的屬性為value使用以下過程:

  1. 按照順序查找第一個(gè)訪問器set<Key>:_set<Key>整胃。如果找到颗圣,使用輸入值(或根據(jù)需要打開包裝值)完成賦值。

  2. 如果沒有找到簡(jiǎn)單的訪問器爪模,并且類方法(是否可以訪問成員變量)accessInstanceVariablesDirectly 返回YES欠啤,那么用如下名稱_<key>, _is<Key>, <key>, or is<Key>查找一個(gè)實(shí)例變量荚藻。如果找到屋灌,直接用輸入值(或打開包裝的值)和完成設(shè)置變量。

  3. 在沒有找到訪問器或?qū)嵗兞繒r(shí)应狱,調(diào)用setValue:forUndefinedKey:共郭,默認(rèn)情況下會(huì)引發(fā)一個(gè)異常,但是NSObject的子類可能提供key-specific的行為疾呻。

搜索可變數(shù)組的模式

mutableArrayValueForKey:的默認(rèn)實(shí)現(xiàn)除嘹,給定一個(gè)key參數(shù)作為輸入,在接收訪問器調(diào)用的對(duì)象內(nèi)部返回一個(gè)名為key的屬性的可變代理數(shù)組岸蜗,使用以下過程:

  1. 查找一對(duì)方法名字像insertObject:in<Key>AtIndex:removeObjectFrom<Key>AtIndex: (對(duì)應(yīng)NSMutableArray原始insertObject:atIndex:removeObjectAtIndex:)尉咕,或方法名稱如 insert<Key>:atIndexes:remove<Key>AtIndexes: (NSMutableArray的insertObjects:atIndexes:removeObjectsAtIndexes:方法)。
    如果對(duì)象至少有一個(gè)插入法和至少一個(gè)移除方法璃岳,返回一個(gè)代理對(duì)象年缎,該對(duì)象通過發(fā)送insertObject:in<Key>AtIndex:悔捶,removeObjectFrom<Key>AtIndex:insert<Key>:atIndexes:单芜,和 remove<Key>AtIndexes:的消息組合給原始的mutableArrayValueForKey:接收者來響應(yīng)NSMutableArray消息蜕该。
    當(dāng)對(duì)象接收到mutableArrayValueForKey:消息時(shí),也會(huì)實(shí)現(xiàn)一個(gè)可選的替換對(duì)象方法洲鸠,該方法的名稱類似于replaceObjectIn<Key>AtIndex:withObject:replace<Key>AtIndexes:with<Key>:堂淡,代理對(duì)象也會(huì)在適當(dāng)?shù)臅r(shí)候使用這些方法,以獲得最佳性能扒腕。
  2. 如果對(duì)象沒有可變數(shù)組方法绢淀,則查找名稱與模式集:匹配的訪問器方法。在這種情況下瘾腰,返回一個(gè)代理對(duì)象響應(yīng)NSMutableArray消息更啄,通過發(fā)出一個(gè)set:消息給原始接收mutableArrayValueForKey:。

注意:此步驟中描述的機(jī)制的效率遠(yuǎn)低于前一步居灯,因?yàn)樗赡苌婕爸貜?fù)創(chuàng)建新的集合對(duì)象祭务,而不是修改現(xiàn)有的集合對(duì)象。因此怪嫌,在設(shè)計(jì)自己的鍵-值編碼兼容的對(duì)象時(shí)义锥,通常應(yīng)該避免使用它。

  1. 如果既沒有找到可變數(shù)組方法岩灭,也沒有找到訪問器拌倍,并且如果接收方的類對(duì)accessInstanceVariablesDirectly直接響應(yīng)YES,那么按照順序搜索一個(gè)具有類似于_<key><key>的實(shí)例變量噪径。
    如果找到了這樣的實(shí)例變量柱恤,則返回一個(gè)代理對(duì)象,該代理對(duì)象將它接收到的每個(gè)NSMutableArray 消息轉(zhuǎn)發(fā)給實(shí)例變量的值找爱,該值通常是NSMutableArray 的一個(gè)實(shí)例或它的一個(gè)子類梗顺。

  2. 如果所有這些都失敗,返回一個(gè)可變的集合代理對(duì)象车摄,每當(dāng)它收到NSMutableArray消息時(shí)寺谤,該對(duì)象向mutableArrayValueForKey:消息的原始接收方發(fā)出setValue:forUndefinedKey: 消息。
    setValue:forUndefinedKey:的默認(rèn)實(shí)現(xiàn)會(huì)引發(fā)NSUndefinedKeyException異常吮播,但子類可能會(huì)覆蓋此行為变屁。

KVC是否有違面向?qū)ο缶幊趟枷耄ㄆ茐拿嫦驅(qū)ο蠓椒ǎ?br> KVC的主要方法 valueForKey: 和 setValue: forKey:, 對(duì) key 是沒有限制的意狠,在知道一個(gè)類內(nèi)部的私有成員變量的名稱情況下粟关,在外部可以通過key訪問和設(shè)置這個(gè)內(nèi)部的私有成員變量,從這個(gè)角度來講环戈,KVC是有OOP思想的闷板。

KVC的賦值和取值過程是怎樣的获列?原理是什么?

  1. KVC 通過 setValue: forKey:setValue: forKeyPath:賦值
    • KVC 在修改值得前后會(huì)分別自動(dòng)調(diào)用willChangeValueForKey和didChangeValueForKey:
    • +(BOOL)accessInstanceVariablesDirectly是否可以訪問成員變量默認(rèn)是 YES蛔垢, 可以訪問击孩,所當(dāng)根據(jù) key找到的是成員變量時(shí)(沒有setKey:和 _setKey:方法)也會(huì)觸發(fā) KVO


      setValue: forKey原理
  1. KVC 通過 valueForKey:取值


    valueForKey:的原理
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鹏漆,隨后出現(xiàn)的幾起案子巩梢,更是在濱河造成了極大的恐慌,老刑警劉巖艺玲,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件括蝠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡饭聚,警方通過查閱死者的電腦和手機(jī)忌警,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秒梳,“玉大人法绵,你說我怎么就攤上這事±业猓” “怎么了朋譬?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)兴垦。 經(jīng)常有香客問我徙赢,道長(zhǎng),這世上最難降的妖魔是什么探越? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任狡赐,我火速辦了婚禮,結(jié)果婚禮上钦幔,老公的妹妹穿的比我還像新娘枕屉。我一直安慰自己,他們只是感情好节槐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布搀庶。 她就那樣靜靜地躺著拐纱,像睡著了一般铜异。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秸架,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天揍庄,我揣著相機(jī)與錄音,去河邊找鬼东抹。 笑死蚂子,一個(gè)胖子當(dāng)著我的面吹牛沃测,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播食茎,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蒂破,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了别渔?” 一聲冷哼從身側(cè)響起附迷,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哎媚,沒想到半個(gè)月后喇伯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拨与,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年稻据,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片买喧。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捻悯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淤毛,到底是詐尸還是另有隱情秋度,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布钱床,位于F島的核電站荚斯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏查牌。R本人自食惡果不足惜事期,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纸颜。 院中可真熱鬧兽泣,春花似錦、人聲如沸胁孙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涮较。三九已至稠鼻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狂票,已是汗流浹背候齿。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慌盯。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓周霉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亚皂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子俱箱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353