當你使用這篇文章描述的標準約定創(chuàng)建一個訪問方法和ivar
時隆敢,KVC協(xié)議的默認實現(xiàn)可以根據(jù)KVC消息定位它們爽茴。對于表示to-many
關(guān)系的集合對象來說也是如此卒稳。但是,如果你實現(xiàn)了集合訪問方法拯啦,我們就可以:
與除了NSArray或NSSet以外的類建立
to-many
關(guān)系.當我們實現(xiàn)了集合方法,key-value getter
的默認實現(xiàn)是返回一個代理對象熔任,該對象調(diào)用這些方法以響應(yīng)后續(xù)收到的NSArray
或NSSet
消息褒链。屬性對象不必是NSArray
或者NSSet
,因為代理對象使用集合方法提供預(yù)期的行為疑苔。改變
to-many
關(guān)系的內(nèi)容可以提高性能. 協(xié)議的默認實現(xiàn)是使用你的集合方法來改變基礎(chǔ)屬性甫匹,而不是重復創(chuàng)建新的集合對象。為集合屬性中的內(nèi)容提供KVO訪問 更多內(nèi)容可以查看 Key-Value Observing Programming Guide.
你可以實現(xiàn)兩類集合訪問方法中的一個夯巷,具體取決于我們希望關(guān)系的行為像索引的有序集合(如NSArray對象)還是無序的赛惩、但唯一的集合(如NSSet對象)。任何情況下趁餐,需要至少實現(xiàn)一組方法來支持對屬性的讀取訪問喷兼。
KVC協(xié)議不會聲明在這部分描述的的方法。相反后雷,NSObject提供的協(xié)議的默認實現(xiàn)是在你的KVC兼容對象中查找這些方法季惯,如KVC方法的搜索模式所述,并使用它們來處理部分KVC消息臀突。
訪問有序集合
你可以添加索引訪問方法勉抓,以提供計算、檢索候学、添加和替換有序關(guān)系中的對象的機制藕筋。底層對象通常是NSArray
或 NSMutableArray
對象,但是如果為對象提供了集合訪問方法梳码,我們可以像處理數(shù)組一樣處理這些屬性隐圾。
有序集合的getters
對于沒有默認的getter的集合屬性伍掀。如果你提供以下索引集合方法,則對于valueForKey:
消息暇藏,協(xié)議的默認實現(xiàn)是返回一個代理對象蜜笤,其行為類似NSArray
,但是會調(diào)用以下集合方法以完成它的工作盐碱。
在現(xiàn)在的OC中把兔,編譯器為每個屬性默認生成了getter,因此默認實現(xiàn)不會創(chuàng)建使用本節(jié)的方法瓮顽∠睾茫可以通過不聲明屬性(僅依靠一個ivar),或者你可以使用
@dynamic
聲明一個屬性來解決這個問題,這表明我們計劃在運行時提供訪問者行為暖混。無論何種方式,編譯器都不會提供默認的getter聘惦,而默認的實現(xiàn)會使用下面的方法
-
countOf<Key>
此方法以NSInteger 的形式返回
to-many
關(guān)系中對象的個數(shù),就像NSArray
的count
方法一樣儒恋。事實上善绎,當?shù)讓訉傩允?code>NSArray時,可以使用該方法提供結(jié)果诫尽。- (NSUInteger)countOfTransactions { return [self.transactions count]; }
-
objectIn<Key>AtIndex:
or<key>AtIndexes:
第一個方法返回
to-many
關(guān)系中指定下標中的對象禀酱,第二個返回一個NSIndexSet
參數(shù)指定索引處的對象數(shù)組。 它們分別對應(yīng)于NSArray
的objectAtIndex:
和objectsAtIndexes:
牧嫉。 只需要實現(xiàn)它們中的一個剂跟。transactions數(shù)組的相應(yīng)方法是:- (id)objectInTransactionsAtIndex:(NSUInteger)index { return [self.transactions objectAtIndex:index]; } - (NSArray *)transactionsAtIndexes:(NSIndexSet *)indexes { return [self.transactions objectsAtIndexes:indexes]; }
-
get<Key>:range:
該方法是可選的,但可以提高性能酣藻。它返回集合指定范圍內(nèi)的對象曹洽,對應(yīng)于
NSArray
的getObjects:range:
方法,transactions數(shù)組的相應(yīng)方法是:- (void)getTransactions:(Transaction * __unsafe_unretained *)buffer range:(NSRange)inRange { [self.transactions getObjects:buffer range:inRange]; }
比如在一個Person
類中辽剧,定義了一個gender
屬性(NSString
對象)送淆,這里我們在.m
文件為其實現(xiàn)相應(yīng)的集合方法,這樣gender
就會表現(xiàn)的跟一個數(shù)組一樣怕轿。
@interface Person : NSObject
@property (nonatomic , copy) NSString *gender;
@end
@implementation Person
// 使用@dynamic修飾偷崩,所以編譯器不會為其生成getter和setter
@dynamic gender;
- (NSUInteger)countOfGender
{
return 5;
}
- (id)objectInGenderAtIndex:(NSUInteger)index
{
return @"w";
}
@end
id obj = [person valueForKey:@"gender"];
NSString *cls = NSStringFromClass([obj class]);
NSLog(@"gender: %@", obj);
NSLog(@"className:%@",cls);
打印信息為
gender: (
w,
w,
w,
w,
w
)
className:NSKeyValueArray
可以看出,雖然我們聲明的是NSString
,但由于我們實現(xiàn)了屬性相應(yīng)的集合方法撞羽,這里屬性已經(jīng)變成了NSArray
類型阐斜。(而且都是不可變數(shù)組,即使我們聲明的是NSMutableArray
類型)
有序集合的改變
使用索引方法來實現(xiàn)可變的 to-many
的關(guān)系必須實現(xiàn)一組不同的方法诀紊。當你提供這些setter方法時谒出,mutableArrayValueForKey:
消息的默認實現(xiàn)是返回一個與NSMutableArray
行為相似的代理對象。這比直接返回一個NSMutableArray
對象更加有效率。
為了使鍵值編碼對象表現(xiàn)跟可變笤喳、有序的 to-many
關(guān)系一樣考赛,需要實現(xiàn)以下這些方法:
-
insertObject:in<Key>AtIndex:
orinsert<Key>:atIndexes:
第一個方法接收要插入的對象和指定應(yīng)該插入的位置。第二個方法插入一個對象數(shù)組到
NSIndexSet
指定的索引處的集合中莉测。這跟NSMutableArray
的insertObject:atIndex:
和insertObjects:atIndexes:
方法類似。只需要其中一個方法唧喉。- (void)insertObject:(Transaction *)transaction inTransactionsAtIndex:(NSUInteger)index { [self.transactions insertObject:transaction atIndex:index]; } - (void)insertTransactions:(NSArray *)transactionArray atIndexes:(NSIndexSet *)indexes { [self.transactions insertObjects:transactionArray atIndexes:indexes]; }
-
removeObjectFrom<Key>AtIndex:
orremove<Key>AtIndexes:
第一個接收一個
NSUInteger
值捣卤,第二個接收一個NSIndexSet
對象,指定要刪除的對象的索引八孝。 這些方法分別對應(yīng)于NSMutableArray
方法removeObjectAtIndex:
和removeObjectsAtIndexes:
董朝。 只需要其中一種方法。- (void)removeObjectFromTransactionsAtIndex:(NSUInteger)index { [self.transactions removeObjectAtIndex:index]; } - (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes { [self.transactions removeObjectsAtIndexes:indexes]; }
-
replaceObjectIn<Key>AtIndex:withObject:
或replace<Key>AtIndexes:with<Key>:
為代理對象提供了一種直接替換集合中對象的方法干跛,而無需連續(xù)刪除一個對象并插入另一個對象子姜。對應(yīng)于
NSMutableArray
的replaceObjectAtIndex:withObject:
和replaceObjectsAtIndexes:withObjects:
方法。當應(yīng)用程序分析顯示性能問題時楼入,可以選擇提供這些方法哥捕。- (void)replaceObjectInTransactionsAtIndex:(NSUInteger)index withObject:(id)anObject { [self.transactions replaceObjectAtIndex:index withObject:anObject]; } - (void)replaceTransactionsAtIndexes:(NSIndexSet *)indexes withTransactions:(NSArray *)transactionArray { [self.transactions replaceObjectsAtIndexes:indexes withObjects:transactionArray]; }
訪問無序集合
通常,這種關(guān)系是NSSet
或NSMutableSet
對象的一個實例嘉熊。 但是遥赚,實現(xiàn)這些方法時,我們可以使用鍵值編碼對該對象進行操作阐肤,就像它是NSSet
的實例一樣凫佛。
無序集合的getter
當你提供以下集合方法以返回集合中對象的數(shù)量的時候,valueForKey:
消息返回一個跟NSSet
行為類似的代理對象孕惜,但是是調(diào)用下面的集合方法來完成愧薛。
-
countOf<Key>
該必需方法返回關(guān)系中條目的數(shù)量,對應(yīng)
NSSet
的count
方法衫画。當?shù)讓訉ο笫?code>NSSet對象毫炉,會直接調(diào)用count
方法。比如- (NSUInteger)countOfEmployees { return [self.employees count]; }
-
enumeratorOf<Key>
這個必需方法返回一個
NSEnumerator
實例,用于遍歷關(guān)系中的條目削罩。- (NSEnumerator *)enumeratorOfEmployees { return [self.employees objectEnumerator]; }
-
memberOf<Key>:
這個方法將作為參數(shù)傳過來的對象和集合中的內(nèi)容進行比較碘箍,并返回匹配的對象,如果沒有找到匹配的對象返回
nil
鲸郊。如果要手動實現(xiàn)比較方法丰榴,通常是使用isEqual:
來比較對象。如果底層對象是NSSet
對象秆撮,可以使用member:
方法- (Employee *)memberOfEmployees:(Employee *)anObject { return [self.employees member:anObject]; }
無序集合的改變
為了使可變無序的to-many
關(guān)系的支持鍵值編碼四濒,需要實現(xiàn)以下方法:
-
add<Key>Object:
oradd<Key>:
添加單個或多個對象。當添加多個對象時,請確保關(guān)系中不存在同等的對象盗蟆。這與NSMutableSet
的addObject:
和unionSet:
方法類似戈二。 只需要其中一個方法:
- (void)addEmployeesObject:(Employee *)anObject {
[self.employees addObject:anObject];
}
- (void)addEmployees:(NSSet *)manyObjects {
[self.employees unionSet:manyObjects];
}
-
remove<Key>Object:
orremove<Key>:
從關(guān)系中刪除單個或者多個對象。這與
NSMutableSet
的removeObject:
和minusSet:
類似喳资。 只需要其中一個方法:- (void)removeEmployeesObject:(Employee *)anObject { [self.employees removeObject:anObject]; } - (void)removeEmployees:(NSSet *)manyObjects { [self.employees minusSet:manyObjects]; }
-
intersect<Key>:
這個方法接收一個
NSSet
作為參數(shù)觉吭,從關(guān)系中刪除不為輸入集與集合中共有的所有對象。這跟NSMutableSet
的intersectSet:
方法等效仆邓。該方法是可選的鲜滩。- (void)intersectEmployees:(NSSet *)otherObjects { return [self.employees intersectSet:otherObjects]; }