1,什么是Key-Value Coding? Key-Value Coding是一种间接访问对象属性的机制,使用字符串标识属性,而不是通过调用实例变量的访问方法。其使用的方法基本都声明自NSKeyValueCoding协议,并被NSObject实现。
Key-Value Coding支持对象属性,也支持标量类型和结构类型。非对象参数和返回类型被自动包装和解包装。
NSKeyValueCoding定义的方法有:
获得属性值的方法:
– valueForKey:
– valueForKeyPath:
– dictionaryWithValuesForKeys:
– valueForUndefinedKey:
– mutableArrayValueForKey:
– mutableArrayValueForKeyPath:
– mutableSetValueForKey:
– mutableSetValueForKeyPath:
– mutableOrderedSetValueForKey:
– mutableOrderedSetValueForKeyPath:
设置属性值的方法:
– setValue:forKeyPath:
– setValuesForKeysWithDictionary:
– setNilValueForKey:
– setValue:forKey:
– setValue:forUndefinedKey:
更改默认行为的方法:
+ accessInstanceVariablesDirectly
验证方法:
– validateValue:forKey:error:
– validateValue:forKeyPath:error:
2,通过KVC获得属性值:方法valueForKey:返回指定键的值,如果没有这个键,接收者会发送给自己一个valueForUndefinedKey:消息。默认的valueForUndefinedKey:消息会引起一个NSUndefinedKeyException,子类化可以重载该行为。
ValueForKeyPath:也是类似的。
方法dictionaryWithValuesForKeys:检索接收者一组keys的值。
提示:集合对象,如NSArray,NSSet,NSDictionary,不能包含nil值,取而代之的,你使用特定对象,NSNull.NSNull来代替nil值。默认的dictionaryWithValuesForKeys:和setValuesForKeysWithDictionary:方法的实现自动转换NSNull 和nil,所以你的对象不用显式地测试NSNull值。
3,通过KVC设置属性值:方法setValue:forKey:。其默认实现自动解包装表示标量和结构类型的NSValue对象。如果指定键不存在,接收者会发送一个setValue:ForUndefinedKey:消息。该方法的默认实现会抛出一个NSUndefinedKeyException异常;子类化可以重载该行为。
SetValuesForKeysWithDictionary:设置接收者字典中的所有键值的值。默认实现调用每个键值对的setValue:forKey:,并用nil代替NSNull。
4,通用的访问格式: -<key> 方法返回一个对象、标量或者结构。 -is<key>支持布尔类型.
5,通用的设置值的格式为 set<Key>:
如果属性是一个非对象类型,你还必须实现nil值的含义。setNilValueForKey:方法在你尝试设置nil给属性的时候被调用。
如下例所示:
- (void)setNilValueForKey:(NSString *)theKey {
if ([theKey isEqualToString:@"hidden"]) {
[self setValue:@YES forKey:@"hidden"];
}
else {
[super setNilValueForKey:theKey];
}
}
6,通用的集合访问格式: mutableArrayValueForKey: 或mutableSetValueForKey:
7,索引访问格式:-countOf<Key>
-objectIn<Key>AtIndex: 或者 -<key>AtIndexes:
-get<Key>:range:
如下例所示:
Listing 4 Example -count<Key> implementation
- (NSUInteger)countOfEmployees {
return [self.employees count];
}
- (id)objectInEmployeesAtIndex:(NSUInteger)index {
return [employees objectAtIndex:index];
}
- (NSArray *)employeesAtIndexes:(NSIndexSet *)indexes {
return [self.employees objectsAtIndexes:indexes];
}
- (void)getEmployees:(Employee * __unsafe_unretained *)buffer range:(NSRange)inRange {
// Return the objects in the specified range in the provided buffer.
// For example, if the employees were stored in an underlying NSArray
[self.employees getObjects:buffer range:inRange];
}
8,可变索引访问:-insertObject:in<Key>AtIndex: 或者 –insert<Key>atIndexes:
-removeObjectFrom<Key>AtIndex: 或-remove<Key>AtIndexes:
-replaceObjectIn<Key>AtIndex:withObject: 或者 –replace<Key>AtIndexes:with<Key>:
- (void)insertObject:(Employee *)employee inEmployeesAtIndex:(NSUInteger)index {
[self.employees insertObject:employee atIndex:index];
return;
}
- (void)insertEmployees:(NSArray *)employeeArray atIndexes:(NSIndexSet *)indexes {
[self.employees insertObjects:employeeArray atIndexes:indexes];
return;
}
- (void)removeObjectFromEmployeesAtIndex:(NSUInteger)index {
[self.employees removeObjectAtIndex:index];
}
- (void)removeEmployeesAtIndexes:(NSIndexSet *)indexes {
[self.employees removeObjectsAtIndexes:indexes];
}
- (void)replaceObjectInEmployeesAtIndex:(NSUInteger)index
withObject:(id)anObject {
[self.employees replaceObjectAtIndex:index withObject:anObject];
}
- (void)replaceEmployeesAtIndexes:(NSIndexSet *)indexes
withEmployees:(NSArray *)employeeArray {
[self.employees replaceObjectsAtIndexes:indexes withObjects:employeeArray];
}
9,无序访问格式:-countOf<Key>
-enumeratorOf<Key>
-memberOf<Key>:
- (NSUInteger)countOfTransactions {
return [self.transactions count];
}
- (NSEnumerator *)enumeratorOfTransactions {
return [self.transactions objectEnumerator];
}
- (Transaction *)memberOfTransactions:(Transaction *)anObject {
return [self.transactions member:anObject];
}
10,可变无序访问:-add<Key>Object: or -add<Key>:
-remove<Key>Object: or -remove<Key>:
-intersect<Key>:
- (void)addTransactionsObject:(Transaction *)anObject {
[self.transactions addObject:anObject];
}
- (void)addTransactions:(NSSet *)manyObjects {
[self.transactions unionSet:manyObjects];
}
- (void)removeTransactionsObject:(Transaction *)anObject {
[self.transactions removeObject:anObject];
}
- (void)removeTransactions:(NSSet *)manyObjects {
[self.transactions minusSet:manyObjects];
}
- (void)intersectTransactions:(NSSet *)otherObjects {
return [self.transactions intersectSet:otherObjects];
}
11,Key-Value Validation:验证
KVC提供了一致的API来验证属性值。
验证方法:如下所示:
isting 1 Validation method declaration for the property name
-(BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError {
// Implementation specific code.
return ...;
}
验证方法有3种可能的输出:
1)对象值是有效的,所以返回YES,不更改值和error
2)对象值无效,并且无法创建一个有效值。这时返回NO,并设置error参数为一个NSError对象表名错误原因。
3)对象值无效,但一个有效值被创建并返回。这时返回YES。并且不设置NSError,但需要设置一个新的ioValue。
-(BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError{
// The name must not be nil, and must be at least two characters long.
if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 2)) {
if (outError != NULL) {
NSString *errorString = NSLocalizedString(
@"A Person's name must be at least two characters long",
@"validation: Person, too short name error");
NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorString };
*outError = [[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN
code:PERSON_INVALID_NAME_CODE
userInfo:userInfoDict];
}
return NO;
}
return YES;
}
重要提示:一个验证方法如果返回NO,需要首先检查outError参数是否是NULL;如果其不是NULL,方法应该设置outError为一个有效的NSError对象。
12,引入验证方法:你可以直接调用验证方法,通过调用validateValue:forKey:error,并指定一个键。该方法的默认实现搜索接收类名字匹配validate<Key>Error:的验证方法,如果这个方法被找到,他将被调用并返回结果。如果没有找到,返回YES。
警告:-set<Key>的实现方法应该永远不要调用验证方法。
13,自动验证:
一般,KVC不自动执行验证—这是你的责任。
其他一些技术确实执行自动验证:Core Data自动执行验证—当管理的对象的context被保存时;等
14,验证标量值:
-(BOOL)validateAge:(id *)ioValue error:(NSError * __autoreleasing *)outError {
if (*ioValue == nil) {
// Trap this in setNilValueForKey.
// An alternative might be to create new NSNumber with value 0 here.
return YES;
}
if ([*ioValue floatValue] <= 0.0) {
if (outError != NULL) {
NSString *errorString = NSLocalizedStringFromTable(
@"Age must be greater than zero", @"Person",
@"validation: zero age error");
NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorString };
NSError *error = [[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN
code:PERSON_INVALID_AGE_CODE
userInfo:userInfoDict];
*outError = error;
}
return NO;
}
else {
return YES;
}
//…