1 概述 數組
1.1 訪問方法 app
Key-value coding(KVC)是一種間接訪問對象屬性的機制,相似鍵值對,經過名字(或鍵)能夠直接得到對象的屬性值。事實上,key-value coding定義了一些模式和規則方法來實現屬性的訪問,這些基本方法在NSKeyValueCoding協議中聲明,同時NSObject默認實現了該協議。 ide
KVC存在兩種形式的訪問方法: 性能
-
getter:獲取對象的屬性值
-
setter:設置對象的屬性值
以下使用KVC來簡化代碼的編寫: 測試
1 - (id)tableView:(NSTableView *)tableview
//
未使用KVC的訪問方法
2
objectValueForTableColumn:(id)column
3 row:(
int)row
4 {
5 ChildObject *child = [childrenArray objectAtIndex:row];
6
if ( [[column identifier] isEqualToString:
@"
name
"] )
7 {
8
return [child name];
9 }
10
if ( [[column identifier] isEqualToString:
@"
age
"] )
11 {
12
return [child age];
13 }
14
if ( [[column identifier] isEqualToString:
@"
favoriteColor
"] )
15 {
16
//
etc...
17
}
18
//
etc...
19
}
20 - (id)tableView:(NSTableView *)tableview
//
使用了KVC的訪問方法
21
objectValueForTableColumn:(id)column
22 row:(
int)row
23 {
24 ChildObject *child = [childrenArray objectAtIndex:row];
25
return [child valueForKey:[column identifier]];
26 }
1.2 訪問屬性 編碼
Key-value coding技術其實就是間接訪問對象的屬性,支持KVC的屬性有三種類型:attributes、to-one relationships和to-many relationships。 spa
1) attributes:該類型屬性是普通類型,有scalar、string和Boolean 等簡單類型,以及一些值對象和不可變類型,如NSNumber和NSColor。 scala
2) to-one relationship:該類型屬性是對象類型,即該類型屬性自己也是一個對象(擁有本身的屬性)。 指針
3) to-many relationships:該類型屬性是一種容器,主要是NSArray 和NSSet容器。 code
2 KVC基礎
2.1 鍵與鍵路徑
1) 鍵(keys)
鍵是字符串類型,用於標識對象的屬性,建的命名必須遵循以下的規則:
a) 必須是 ASCII編碼的。
b) 必須以小寫字母打頭。第一個字母能夠是下劃線,但不能是數字,也不能是大寫字母。
c) 不能包含空格。
2) 鍵路徑(Key Paths)
鍵路徑也是一個鍵,其經過點操做符來隔開不一樣對象。訪問一個屬性的屬性,就須要使用點標記指定一個更復雜的鍵路徑。
好比:
1 [foo valueForKeyPath:@」someMember.someAttributeonMember」];
2 鍵路徑訪問 foo 對象的 someMember 特性,在 someMember 所屬的類上查找someAttributeOnMember屬性,並返回存儲在這裏的值。
2.2 Getter方法
KVC提供以下方法來獲取對象的屬性值:
1) valueForKey:方法
經過向該方法傳遞一個key(標識符),可以得到對象的屬性值。若沒有相應的訪問方法或沒有健變量,則所屬對象將調用自身的valueForUndefinedKey方法,該方法默認拋出NSUndefinedKeyException異常。
2) valueForKeyPath:方法
經過向該方法傳遞一個key path,可以得到對象中屬性的屬性值。若沒有鍵路徑,一樣會調用自身的valueForUndefinedKey方法。
3) dictionaryWithValuesForKeys:方法
用一個標識符數組(字符串類型)返回一個NSDictionary對象。
2.3 Setter方法
KVC提供一系列方法設置對象的屬性:
1) setValue:forKey:方法
經過向該方法傳遞key和value,從而可以設置對象的屬性值,而且該方法自動實現wrapping和unwrapping。若對象中不存在key標識符或變量,則會調用該對象的setValue:forUndefinedKey:方法,該方法的默認實現是拋出NSUndefinedKeyException類型的異常。
2) setValue:forKeyPath:方法
一樣是設置對象的屬性,不一樣的是該方法可以操縱key path。
3) setValuesForKeysWithDictionary:方法
經過向該方法傳遞一個NSDictionary對象,從而設置對象的屬性。
2.4 訪問實例
以下簡單建立Myclass類,並在該類中定義了兩個屬性,接着經過KVC進行訪問對象的屬性。
1) 類定義
@interface MyClass : NSObject
@property NSString *
string;
@property NSInteger integer;
@property MyClass *instance;
@end
2) 鍵訪問
1
int main(
int argc,
const
char * argv[])
2 {
3 MyClass *myInstance = [[MyClass alloc] init];
4
5 [myInstance setValue:
@"
hello world
" forKey:
@"
string
"];
6 [myInstance setValue:[NSNumber numberWithInt:
100] forKey:
@"
integer
"];
7
8 NSLog(
@"
%@
",[myInstance valueForKey:
@"
string
"]);
9 NSLog(
@"
%@
",[myInstance valueForKey:
@"
integer
"]);
10
return
0;
11 }
12 輸出:
13
2016-
04-
06
21:
04:
32.533 KVC[
1134:
94328] hello world
14
2016-
04-
06
21:
04:
32.534 KVC[
1134:
94328]
100
3) 鍵路徑訪問
1
int main(
int argc,
const
char * argv[])
2 {
3 MyClass *myInstance = [[MyClass alloc] init];
4 MyClass *anotherInstance = [[MyClass alloc] init];
5 anotherInstance.instance = myInstance;
6
7 [anotherInstance setValue: [NSNumber numberWithInt:
222] forKeyPath:
@"
instance.integer
"];
8 NSLog(
@"
%@
",[anotherInstance valueForKeyPath:
@"
instance.integer
"]);
9
10
return
0;
11 }
12 輸出:
13
2016-
04-
06
21:
13:
46.432 KVC[
1253:
99388]
222
3 存取方法
雖然可使用KVC中的訪問方法valueForKey:和setValue:forKey:來操做對象的屬性,但須要實現屬性的存取方法來供KVC訪問方法調用。
3.1 普通模式
3.1.1 取方法(getter)
對於屬性的取方法有兩種形式,對於不一樣類型的屬性能夠實現以下兩種之一:
如某類中的hidden屬性爲bool類型,能夠以下兩種方式實現其取方法:
1 - (BOOL)hidden
2 {
3
//
Implementation specific code.
4
return ...;
5 }
6 - (BOOL)isHidden
7 {
8
//
Implementation specific code.
9
return ...;
10 }
3.1.2 存方法(setter)
爲了使用KVC的setValue:forKey:方法來設置屬性值,須要實現對象的set<Key>:存方法。以下所示hidden屬性的存方法實現。
1 - (
void)setHidden:(BOOL)flag
2 {
3
//
Implementation specific code.
4
return;
5 }
若設置的屬性值爲non-object類型(即爲標量),那麼還需考慮傳遞給存方法的參數爲nil的狀況。其中在Objective-C中,若給標量屬性的存方法傳遞nil參數,那麼將調用該對象的setNilValueForKey:方法。從而工程師能夠重載該方法來從新設置新的值,不然將拋出異常。
如在給setHidden方法傳遞nil參數時,須要將其重定向爲YES值,那麼能夠進行以下重載setNilValueForKey:方法:
1 - (
void)setNilValueForKey:(NSString *)theKey
2 {
3
if ([theKey isEqualToString:
@"
hidden
"])
4 {
5 [self setValue:[NSNumber numberWithBool:YES] forKey:
@"
hidden
"];
6 }
else
7 [super setNilValueForKey:theKey];
8 }
3.2 集合模式
若對象的屬性是一個集合,也能夠簡單就實現-<key>和-set<Key>:兩種存取方法來訪問集合對象。若須要訪問集合中的元素,那麼須要實現更多的方法。
能夠簡單將Objective-C的集合類型分爲兩種:有序和無序。從而集合元素的存取方法也相應有兩種形式:索引存取器方法和無序存取器方法。
3.2.1 有序集合
索引方法定義了集合屬性的計數、查詢、添加和替換等機制,其中有序集合有種兩種類型:NSArray和NSMutableArray。
1) 取元素方法(不可變集合)
不可變集合只能獲取集合中的元素,從而只需實現以下的方法:
-
-countOf<Key>:必須實現,此方法的功能相似NSArray類的count方法。
-
-objectIn<Key>AtIndex:或-<key>AtIndexes:必須實現之一,一樣相似於NSArray類的objectAtIndex: and objectsAtIndexes:方法。
-
-get<Key>:range:可選,其功能相似於NSArray類的getObjects:range方法。
如在某類中有個employees集合,從而可實現上述四個方法:
1 - (NSUInteger)countOfEmployees
2 {
3
return [employees count];
4 }
5 - (id)objectInEmployeesAtIndex:(NSUInteger)index
6 {
7
return [employees objectAtIndex:index];
8 } -
9 (NSArray *)employeesAtIndexes:(NSIndexSet *)indexes
10 {
11
return [employees objectsAtIndexes:indexes];
12 }
13 - (
void)getEmployees:(Employee **)buffer range:(NSRange)inRange
14 {
15
//
Return the objects in the specified range in the provided
16
//
buffer. For example, if the employees were stored in an
17
//
underlying NSArray
18
[employees getObjects:buffer range:inRange];
19 }
2) 存元素方法(可變集合)
對於可變集合(mutable)自己是能夠修改集合的元素,所以除了須要實現上述的取元素方法,同時還須要實現以下的存元素方法:
a) -insertObject:in<Key>AtIndex: 或 -insert<Key>:atIndexes:
至少實現之一,其功能相似於NSMutableArray類的insertObject:atIndex和insertObjects:atIndexes:。
b) -removeObjectFrom<Key>AtIndex: 或 -remove<Key>AtIndexes:
至少實現之一,其功能相似於NSMutableArray類的removeObjectAtIndex:和removeObjectsAtIndexes:。
c)-replaceObjectIn<Key>AtIndex:withObject: 或 -replace<Key>AtIndexes:with<Key>:
可選的,如有性能上的要求能夠實現其。
如在某類中有個employees集合,從而可實現上述6個方法:
1 - (
void)insertObject:(Employee *)employee inEmployeesAtIndex:(NSUInteger)index
2 {
3 [employees insertObject:employee atIndex:index];
4
return;
5 }
6 -(
void)insertEmployees:(NSArray *)employeeArray atIndexes:(NSIndexSet *)indexes
7 {
8 [employees insertObjects:employeeArray atIndexes:indexes];
9
return;
10 }
11 - (
void)removeObjectFromEmployeesAtIndex:(NSUInteger)index
12 {
13 [employees removeObjectAtIndex:index];
14 }
15 - (
void)removeEmployeesAtIndexes:(NSIndexSet *)indexes
16 {
17 [employees removeObjectsAtIndexes:indexes];
18 }
19 - (
void)replaceObjectInEmployeesAtIndex:(NSUInteger)index withObject:(id)anObject
20 {
21 [employees replaceObjectAtIndex:index withObject:anObject];
22 }
23 -(
void)replaceEmployeesAtIndexes:(NSIndexSet *)indexes withEmployees:(NSArray *)employeeArray
24 {
25 [employees replaceObjectsAtIndexes:indexes withObjects:employeeArray];
26 }
3.2.2 無序集合
1) 取元素方法(不可變集合)
對於無序的集合來講,通常的取元素方法是獲取迭代器和判斷是否爲成員對象。爲了知足只讀須要,必須實現以下全部的方法。
-
-countOf<Key>:相似NSSet類的count方法;
-
-enumeratorOf<Key>:相似NSSet類的objectEnumerator方法;
-
-memberOf<Key>:相似NSSet類的member方法;
以下是實現例子:
1 - (NSUInteger)countOfTransactions
2 {
3
return [transactions count];
4 }
5 -(NSEnumerator *)enumeratorOfTransactions
6 {
7
return [transactions objectEnumerator];
8 }
9 -(Transaction *)memberOfTransactions:(Transaction *)anObject
10 {
11
return [transactions member:anObject];
12 }
2) 存元素方法(可變集合)
爲了容易且高效的利用mutableSetValueForKey:方法來訪問無序集合的元素,須要實現以下方法:
a) -add<Key>Object: 或 -add<Key>:
至少實現二者之一,功能相似於NSMutableSet類的addObject:方法。
b) -remove<Key>Object: 或 -remove<Key>:
至少實現二者之一,功能相似於NSMutableSet類的removeObject:方法。
c) -intersect<Key>:
可選類型,功能相似於NSSet 類的intersectSet: 方法。
以下的測試例子:
1 - (
void)addTransactionsObject:(Transaction *)anObject
2 {
3 [transactions addObject:anObject];
4 }
5 -(
void)addTransactions:(NSSet *)manyObjects
6 {
7 [transactions unionSet:manyObjects];
8 }
9 - (
void)removeTransactionsObject:(Transaction *)anObject
10 {
11 [transactions removeObject:anObject];
12 }
13 -(
void)removeTransactions:(NSSet *)manyObjects
14 {
15 [transactions minusSet:manyObjects];
16 }
17 - (
void)intersectTransactions:(NSSet *)otherObjects
18 {
19
return [transactions intersectSet:otherObjects];
20 }
4 搜索模式
4.1 簡單屬性
4.1.1 默認setValue:forKey
當爲了設置對象的某個屬性,而調用其setValue:forKey:方法時,將按以下順序執行搜素:
1) 首先會搜索接收對象的set<Key>:方法,即setValue:forKey:方法所屬的對象。
2) 若未找到匹配的方法,且接收對象的accessInstanceVariablesDirectly方法會返回YES,那麼將在接收對象中按序搜素以這樣命名的實例變量:_<key>、_is<Key>、<key>、和is<Key>。
3) 若找到匹配的方法,那麼會調用匹配的方法;若須要能夠設置Non-Object值。
4) 若沒有匹配方法或實例變量被發現,那麼會調用接收對象的setValue:forUndefinedKey:方法。
4.1.2 默認valueForKey
當爲了訪問對象的某個屬性,而調用其valueForKey:方法時,將按以下的順序執行搜索:
a) 首先會在接收對象中按序搜索get<Key>、<key>、和is<Key>方法,若找到其中之一的方法,而且該方法返回的是Objective-C類型的指針,則直接返回;如果找到的方法是返回支持NSNumber的標量類型,那麼將執行轉換。
b) 若未找到匹配的方法,則在接收對象中按序搜素countOf<Key>、objectIn<Key>AtIndex:、<key>AtIndexes:方法。
c) 若仍未找到匹配的方法,則在接收對象中按序搜索countOf<Key>、enumeratorOf<Key>、和memberOf<Key>:方法。
d) 若未找到匹配的方法,且接收對象的accessInstanceVariablesDirectly方法會返回YES,那麼將在接收對象中按序搜素以這樣命名的實例變量:_<key>、_is<Key>、<key>、和is<Key>。
e) 若上述全部狀況都未發現,那麼將調用接收對象的valueForUndefinedKey:方法。