第二十章 鍵值編碼

什么是鍵值編碼

什么是鍵值編碼

訪問實例變量可以通過訪問器訪問,也可以將屬性設(shè)置為@public。

與此相對,鍵值編碼(key-value coding)是指,將表示對象包含的信息的字符串作為鍵值使用,來間接訪問該信息的方式。鍵值編碼提供了非常強大的功能,基本上,只要存在訪問器方法,聲明屬性或?qū)嵗兞浚涂梢詫⑵涿种付樽址畞碓L問。本章中,可以訪問,設(shè)定的對象狀態(tài)的值稱為屬性(property)。

之所以說鍵值編碼的訪問是間接的,是因為以下兩點:

也可以在運行中確定作為鍵的字符串。

使用者無法知道實際訪問屬性的方法。

鍵值編碼的基本處理

鍵值編碼的必須方法在非正式協(xié)議NSKeyValueCoding中聲明,這些默認在NSObject中實現(xiàn)。

有訪問器的屬性會使用該訪問器,沒有訪問器的屬性也可以設(shè)定值和訪問。而且變量obj也可以為id類型。

訪問屬性

鍵值編碼的方法的行為

下面的講述圍繞name進行。以下劃線開始的名字不能用于方法名或?qū)嵗兞棵4送?,也不要使用以get開始的名字。

接收器中如果有name訪問器則使用它。

沒有訪問器時,使用接收器的類方法accessInstanceVariablesDirectly來查詢。返回YES時,如果存在實例變量name時(或_name,isName,_isName等)則返回其值。使用引用計數(shù)的方式時,實例變量如果是對象,則舊值會被自動釋放,新值被保存并帶入。

既沒有訪問器也沒有實例變量時,將引起接收器調(diào)用方法setValue:forUndefinedKey:

應該返回的值如果不是對象,則返回被適當?shù)膶ο蟀b的值。

但是,如果接收器包含與帶索引的訪問器模式一致的方法,則將返回有數(shù)組對象行為代理(proxy)對象。

accessInstanceVariablesDirectly只要該方法返回YES,實例變量的可見屬性即使有@private修飾,也可以訪問。

setValue:forKey:如果設(shè)定值失敗,則調(diào)用下面的方法。

- (void)setValue:(id)value forUndefinedKey:(nonnullNSString *)key

//不能設(shè)置鍵字符串key對應的屬性值時,從方法setValue:forKey中調(diào)用該方法。默認情況下,該方法的執(zhí)行會觸發(fā)異常NSUndefinedKeyException。不過,通過在子類中修改定義,就可以返回其他對象。

鍵值編碼比訪問器以及實例變量名更具靈活性。

由于鍵值編碼所接收的對象都是id類型,因此,在該部分中,編譯時不會進行仔細的類型檢查。所以一定要注意不要傳入與屬性不符的對象。

使用鍵值編碼的程序如何執(zhí)行,不是由靜態(tài)解析源碼語法的結(jié)果決定的,而是使用程序運行時包含的信息動態(tài)決定的。與實例變量的可視屬性不同,有方法決定是否可以訪問實例變量這一點,會使人感覺有損一致性??傊I值編碼這一強大的功能就像一把雙刃劍,也伴隨著危險,因此不可以濫用。

屬性值的自動轉(zhuǎn)換

將屬性中單純的數(shù)值,也就是整數(shù)或?qū)崝?shù),布爾值這樣的數(shù)據(jù)稱為標量(scalar)值。將標量值,結(jié)構(gòu)體,字符串或NSNumber等常數(shù)對象稱為屬性(attribute)。

方法setValue:在返回值為標量值或結(jié)構(gòu)體時,會返回將其自動包裝的對象。另一方面,為了給setValue:forKey:傳入值,也需要使用適當?shù)膶ο髞戆b。

單純的數(shù)值用NSNumber來包裝,結(jié)構(gòu)體用NSValue類的實例來包裝。屬性值如果為對象,則可以將nil作為值傳遞。另一方面,當將nil作為值傳遞時,setNilValueForKey:方法將被發(fā)送給接收器。

- (void)setNilValueForKey:(NSString *)key

//執(zhí)行該方法將產(chǎn)生NSInvalidArgumentException異常

字典對象和鍵值編碼

字典類NSDictionary和NSMutableDictionary包含了協(xié)議NSKeyValueCoding的方法,使用它們可以進行鍵值編碼。

- (id)valueForKey:(NSString *)key

//鍵字符串開頭不是@時,將調(diào)用方法objectForKey:。如果開頭為”@“,則將去除開頭字符后剩余的字符串作為鍵,調(diào)用超類的方法valueForKey:。

NSMutableDictionary中定義了以下的方法:

- (void)setValue:(id)value forKey:(NSString *)key

//一般會調(diào)用方法setObject:forKey:,參數(shù)value為nil時,調(diào)用方法removeObjectForKey:刪除鍵對應的對象。

根據(jù)鍵路徑進行訪問

屬性為對象時,該對象還可能持有屬性。在鍵值編碼中,使用某個鍵訪問獲得某個屬性對象后,如果希望再用別的鍵來訪問該對象,可采用如下方法:

idname = [aGroup valueForKey:@“l(fā)eader.name"];

像這樣,用“.”連接鍵表示的字符串稱為鍵路徑(key path)。只要能找到對象,點和鍵多長都沒有關(guān)系。

聲明屬性的點是運算符,而這里的鍵路徑則是一個字符串。

使用鍵路徑訪問屬性的方法如下:(略)

一對一關(guān)系和一對多關(guān)系

使用鍵(或鍵路徑)訪問時,我們將對象確定為一個的屬性稱為指定一對一關(guān)系(to-one relationship)的屬性,將屬性值為數(shù)組或集合的屬性稱為指定一對多關(guān)系(to-many relationship)的屬性。

如果鍵對應一個對象,那么也是一對一關(guān)系。

關(guān)于一對多關(guān)系屬性的訪問,更改,需要留意以下幾點:

1.使用集合元素對象持有的鍵訪問一對多關(guān)系屬性時,鍵對應的屬性被作為數(shù)組或集合返回。

2.使用集合元素對象持有的鍵設(shè)定一對多關(guān)系屬性時,各元素對象鍵對應的屬性全都被更改。

數(shù)組對象和鍵值編碼

數(shù)組類NSArray和NSMutableArray以及集合類NSSet和NSMutableSet都包含協(xié)議NSKeyValueCoding的方法,也都有鍵值編碼。

- (id)valueForKey:(NSString *)key//以key為參數(shù),對集合的各元素調(diào)用方法valueForKey:后返回數(shù)組(NSSet時返回集合)。對各成員適用方法valueForKey:,返回nil時,則包含NSNull實例。

- (void)setValue:(id)value forKey:(NSString *)key//對集合各元素調(diào)用方法setValue:forKey:。需要注意的是,即使集合對象自身不可以改變,也能調(diào)用該方法。

一對多關(guān)系的訪問

帶索引的訪問器模式

即使是非數(shù)組對象,如果有某個模式的訪問器,也可以進行像數(shù)組一樣的鍵值編碼操縱。該訪問器模式稱為帶索引的訪問器模式(indexed accessor pattern)。

下面是兩個方法等實現(xiàn)。下劃線部分會輸入字符串。

- (NSUInteger)countOf___;

- (id)objectIn___AtIndex:(NSUInteger)index;

為了提高運行效率,除上述兩個方法外,還可以實現(xiàn)下面的方法。這也是和數(shù)組類簇中的getObjects:range:相同的方法。

- (void)get___:(id__unsafe_unretained[])aBuffer range:(NSRange)aRange;

一對多關(guān)系的可變訪問

獲得可變數(shù)組對象的方法。

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key

//返回相當于用鍵字符串指定的一對多關(guān)系的屬性的可變數(shù)組。操作被返回的數(shù)組與操作屬性同時進行。

- (NSMutableArray *)NSMutableArrayValueForKeyPath:(NSString *)keyPath

//接收器屬性在鍵路徑中指定。

用該方法操作屬性時,除了之前提到的訪問常數(shù)數(shù)組需要添加的兩個方法之外,還需要實現(xiàn)用來插入和刪除的方法。下劃線部分加入了鍵字符串(通常為復數(shù)形式)。使用這些方法并通過鍵值編碼訪問時,內(nèi)部的代理就會作為數(shù)組進行操作了。

- (void)insertObject:(id)obj in___AtIndex:(NSUInteger)index;

- (void)removeObjectFrom___AtIndex:(NSUInteger)index;

不僅是上述兩個方法,通過實現(xiàn)下面的方法也可以實現(xiàn)屬性的可變訪問。

- (void)set___:(id)anArray;

在實現(xiàn)該方法時,通過使用被作為參數(shù)傳入的數(shù)組元素對象,就可以置換一對多關(guān)系的全部屬性內(nèi)容。

在添加了插入和刪除的方法的基礎(chǔ)上,如果能實現(xiàn)下面的方法,則將能顯著改善運行效果。

- (void)replaceObjectIn___AtIndex:(NSUInteger)index withObject:(id)obj;

如果上述方法都沒有實現(xiàn),那么當存在與鍵字符串同名的實例變量且該變量又是可變數(shù)組的對象時,方法mutableArrayValueForKey:將直接返回該值。

KVC標準

驗證屬性值

在某些情況下,如果預期之外的對象被設(shè)定了屬性值,那么就可能出現(xiàn)問題。

因此,在為某屬性帶入對象前,可以使用相應的方法來驗證,但是驗證方法不能自動調(diào)用(使用Cocoa綁定時,可以設(shè)定自動驗證),因此,在訪問屬性前,必須自行調(diào)用該方法。

驗證某鍵字符串的屬性值的方法可按如下形式定義。下劃線中寫入鍵字符串。參數(shù)ioValue為需要驗證的對象的指針。參數(shù)outError被用來當驗證結(jié)果中存在時返回出錯信息。

- (BOOL)validate___:(inoutid*)ioValue error:(outNSError **)outError;

對象有問題時,但是能將對象修正為有效值時,方法會創(chuàng)建新的對象,并取代原對象將新對象帶入ioValue。參數(shù)outError不變,返回值為YES。

對象有問題且不能修正時,則創(chuàng)建錯誤對象并將其帶入?yún)?shù)outError。方法返回NO。

設(shè)定屬性訪問值的訪問器方法(set___:)不能調(diào)用驗證方法。

運行時,鍵會被動態(tài)地賦值給對象的情況下,不能在代碼中使用上述方法名。此時,可以使用下面的兩個方法。

- (BOOL)validateValue:(inoutid*)ioValue forKey:(NSString *)key error:(outNSError**)outError

//使用指定鍵尋找validate___:error:的驗證方法并調(diào)用,如果不存在這樣的驗證方法,則返回YES。

- (BOOL)validateValue:(inoutid*)ioValue forKeyPath:(NSString *)keyPath error:(outNSError**)outError

//實際被調(diào)用的驗證方法并不是該方法的接收器,而是與最后的鍵元素相對應的屬性的驗證方法。

鍵值編碼的準則

如果可以使用鍵值編碼來訪問某個屬性,則稱該屬性是鍵值編碼的準則,或稱為KVC準則(compliance)。反之,如果知道某屬性為KVC準則,那么就可以編寫使用鍵值編碼的程序。KVC準則和協(xié)議適用的概念不同,它不是以類為單位,而是討論以各個屬性為單位是不是準則的問題。

要使某屬性為KVC準則,就必須實現(xiàn)能使用valueForKey:方法的訪問器。當屬性可變時,還需要與方法setValue:forKey:相應的訪問器。下面列舉一些具體的條件:

property為屬性(標量值或單純型的對象)或一對一關(guān)系時,要想成為KVC準則,就需要滿足如下條件。屬性名為“name”。

1.實現(xiàn)了name或isName訪問器方法?;蛘甙琻ame(或_name)實例變量。

2.可變屬性時,還需要實現(xiàn)setName:方法。需要執(zhí)行鍵值驗證時,要實現(xiàn)驗證方法(validateName:error:)。但是,setName:方法中不能調(diào)用驗證方法。

屬性為一對多關(guān)系時,要想成為KVC準則。需滿足如下條件。屬性名為“names”。

1.實現(xiàn)了返回數(shù)組的names方法。或者持有包含names(或names)數(shù)組對象的實例變量或者實現(xiàn)了帶索引的訪問器模式的方法countOfNames以及objectInNamesAtIndex

2.當一對多關(guān)系的屬性可變時

持有返回可變數(shù)組對象的names方法。或者

實現(xiàn)了帶索引的訪問器模式的方法

insertObject:inNamesAtIndex:以及removeObjectFromNamesAtIndex:。

當然,在實現(xiàn)帶索引的訪問器模式的方法時,為改善執(zhí)行效率,也可以添加其他方法來實現(xiàn)。

鍵值觀察

鍵值觀察(key-value-observing),即某個對象的屬性改變時通知其他對象的機制。有時也記作KVO。

對被視察對象來說,鍵值觀察就是注冊想要監(jiān)視的屬性的鍵路徑和觀察者。當屬性改變時,觀察者會接收到消息。

僅僅在使用鍵值編碼準則來訪問訪問器或?qū)嵗兞康那闆r下,才可以監(jiān)視屬性的變化。在方法內(nèi)直接改變實例變量值時,就不能監(jiān)視了。

NSObject中提供了鍵值觀察所必需的方法,頭文件Foundation/NSKeyValueObserving.h中將其定義為了非正式協(xié)議。

使用引用計數(shù)管理方式時需要注意一些事項。注冊屬性監(jiān)視時,不需要持有觀察者及監(jiān)視對象的屬性。還有,如果在不刪除注冊信息的情況下將關(guān)聯(lián)對象釋放,那么隨著屬性的變更,就可能會發(fā)生訪問已釋放了的對象的危險。

一對多的屬性監(jiān)視

監(jiān)視方法與前述方法相同。但有一點必須注意,那就是一對多關(guān)系為數(shù)組類型時使用方法mutableArrayValueForKey:獲得對象,集合類型同樣如此,如果不修改值是不能被監(jiān)視的。

依賴鍵的登記

某屬性值伴隨著同一對象的其他屬性的改變而改變是常有的事情。通過事先將這樣的依賴關(guān)系在類中注冊,那么即使屬性值間接的發(fā)生了變化,也會發(fā)送通知。為此,需要使用下面的類方法:(略)

Cocoa綁定描述

目標-行為-模式的弱點

在面向?qū)ο蟪绦蛟O(shè)計中,應盡可能地去除特定的類與類之間的關(guān)系,定義低耦合的類。也就是說,某個類改變時,最好不會影響其他的類,否則就不是我們期望的編程。但是,就算完美地定義了每個類,它們間如果不能聯(lián)動也就不能實現(xiàn)功能。一個典型的例子就是,窗體及窗體上面的按鈕菜單等GUI組件也是對象,它們間如果不連接,程序就不能運行。

這樣看來,除了各個類或GUI組件原本就應該有的功能之外,還需要為它們之間的聯(lián)動補充必要的代碼。而這樣的代碼就像是把元素粘在一起的膠水,因此稱為膠水代碼(glue code)。膠水代碼既可以被寫成專門的類,也可以滲透在各個關(guān)聯(lián)的類中。

Mac OSX從第一個版本NexTstep開始,就有了將GUI組件與使用組件的對象結(jié)合在一起的開發(fā)工具Interface Builder?!澳z”的部分不需要特意編寫代碼,使用Interface Builder的的GUI環(huán)境就可以簡單地實現(xiàn),因此能大幅提高編程效率。Interface Builder中中GUI組件被操作時,會預先指定向哪個對象發(fā)送什么消息,這里,Objective-C靈活的消息發(fā)送機制發(fā)揮著非常大的作用。以上就是我們說明過的目標行為模式。

但是,“從操作組件向目標發(fā)送消息”這樣的方式在很多情況下都是無能為力的。特別是當值改變時,為了使多個對象聯(lián)動,必須書寫專門的代碼。圖20-2(a)希望實現(xiàn)的是,繪圖用的參數(shù)可從滑塊或文本域輸入中獲得,并根據(jù)值的變化改變各種顯示,這樣的情況很常見,但只用Interface Builder連接解決不了這樣的問題,還有必要使用專門的對象。程序自身雖然簡單,但這樣的組合多了的話,就要寫大量相似的代碼。

什么是Cocoa綁定

從Mac OS X 10.3起開始引入的Cocoa綁定(Cocoa binding)是指,使用鍵值編碼和鍵值觀察的組合,在多個對象間共享屬性值的變化的機制。(在iOS中不可以使用)

上圖(b)為例說明了它的概要。

Cocoa綁定所需的方法

使用Cocoa綁定,將某對象綁定到控制器屬性時,該對象必須實現(xiàn)下面的方法。該方法用頭文件AppKit/NSKeyValueBinding.h中的非正式協(xié)議NSKeyValueBindingCreation來聲明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • 國家電網(wǎng)公司企業(yè)標準(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 12,297評論 6 13
  • 開年初八上班就似乎感覺不到年味了,一切工作生活變的日常如初。情人節(jié)郭先生送來的玫瑰和生日的三個蛋糕,似乎為這個二月...
    行走的藍天白云閱讀 266評論 0 0
  • 清晨,熹微的光照進墻角的窄條窗,他緩慢醒轉(zhuǎn),周圍的鄰居們剛剛?cè)雺?,隔壁青銅方壺上的夔龍還用眨眼朝他道早安。這是一天...
    風華布衣閱讀 260評論 0 2
  • 人可矯情一陣子,但不能矯情一輩子,在這個社會上,你不成長,必將承受不成熟的后果。最近很長一陣子上班時間我...
    小冷小姐閱讀 632評論 1 1

友情鏈接更多精彩內(nèi)容