iOS - Key Value Coding
KVO 一种能够让对象的属性进行间接访问的机制
KVO 的实现由非正式协议 NSKeyValueCoding 拟定
只要继承了 NSObject 的对象都遵循 NSKeyValueCoding 协议,并提供了默认实现
参考链接
一、Using Key-Value Coding Compliant Objects
-
Accessing Object Properties
- 访问对象属性 -
Accessing Collection Properties
- 集合类型属性 -
Using Collection Operators
- 在集合类型中使用集合运算符,在 key 中插入运算符 -
Access non-object properties
- 访问非对象属性 -
Access properties by key path
- 通过 Keypath 访问属性
1、Accessing Object Properties
属性的三大类型
-
Attributes,简单类型,例如标量、字符串、布尔类型;值对象例如 NSNumber 和其他不可变类型 NSColor
@property (nonatomic) NSNumber* currentBalance;
-
To-one relationships,引用 owner 的地址,owner 可以改变,但是不影响对象本身
@property (nonatomic) Person* owner;
-
To-many relationships,集合对象例如 NSArray、NSSet
@property (nonatomic) NSArray< Transaction* >* transactions;
>> 通过 Key 或 KeyPath 访问属性
-
Key,替换
setCurrentBalance:
方法;[myAccount setValue:@(100.0) forKey:@"currentBalance"];
-
KeyPath,使用点路径
owner.address.street
;[self setValue:@"street" forKeyPath:@"owner.address.street"];
在 Swift 3 中新增
#keypath
表达式
>> Getting Attribute Values Using Keys
-
valueForKey:
- 如果找不到对应的 key,调用valueForUndefinedKey:
-
valueForKeyPath:
-relationship.property
(至少一个relationship
),使用valueForKey:
获取每个 replationship 的目标对象直到最后一个 -
dictionaryWithValuesForKeys:
- 为数组中的每一个 key 调用valueForKey:
,并返回 NSDictionary
valueForUndefinedKey:
- 默认抛出 NSUndefinedKeyException
异常,可重写该方法并自定义处理
>> Setting Attribute Values Using Keys
-
setValue:forKey:
- 对于标量或者结构体,自动解包为 NSNumber 和 NSValue;如果找不到对应的 key,调用setValue:forUndefinedKey:
-
setValue:forKeyPath:
-relationship.property
(至少一个relationship
),使用setValue:forKey:
获取每个replationship
的目标对象直到最后一个 -
setValuesForKeysWithDictionary:
- 为每一个键值对调用setValue:forKey:
setValue:forUndefinedKey:
- 默认抛出 NSUndefinedKeyException
异常,可重写该方法并自定义处理
当尝试给一个非对象类型赋值 nil 时,会发送 setNilValueForKey:
消息,默认抛出 NSInvalidArgumentException
>> eg: 通过属性的值(字符串)获取对应值的属性名的属性
- (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;
}
- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row
{
return [[self.people objectAtIndex:row] valueForKey:[column identifier]];
}
2. Accessing Collection Properties
-
mutableArrayValueForKey:
/mutableArrayValueForKeyPath:
-
mutableSetValueForKey:
/mutableSetValueForKeyPath:
-
mutableOrderedSetValueForKey:
/mutableOrderedSetValueForKeyPath:
3. Using Collection Operators
在 valueForKeyPath:
的 key path 中嵌入集合运算符
![](https://img.haomeiwen.com/i1929662/6ce2a7723a975f3a.png)
在三种场景下使用
>> Aggregation Operators
根据集合中的对象,输出单个值(NSNumber)
Operators | Description | Expression |
---|---|---|
@avg |
平均值 | NSNumber *transactionAverage = [self.transactions valueForKeyPath:@"@avg.amount"]; |
@count |
数量 | NSNumber *numberOfTransactions = [self.transactions valueForKeyPath:@"@count"]; |
@max |
最大值 | NSDate *latestDate = [self.transactions valueForKeyPath:@"@max.date"]; |
@min |
最小值 | NSDate *earliestDate = [self.transactions valueForKeyPath:@"@min.date"]; |
@sum |
总和 | NSNumber *amountSum = [self.transactions valueForKeyPath:@"@sum.amount"]; |
>> Array Operators
返回一个子集数组
Operators | Description | Expression |
---|---|---|
@distinctUnionOfObjects |
获取 transactions 中 transaction.payee 的值,结果去重 | NSArray *distinctPayees = [self.transactions valueForKeyPath:@"@distinctUnionOfObjects.payee"]; |
@unionOfObjects |
获取 transactions 中 transaction.payee 的值 | NSArray *payees = [self.transactions valueForKeyPath:@"@unionOfObjects.payee"]; |
>> Nesting Operators
返回 NSArray 或 NSSet,嵌套数组或集合使用
NSArray* moreTransactions = @[<# transaction data #>];
NSArray* arrayOfArrays = @[self.transactions, moreTransactions];
arrauOfArrays 是一个嵌套数组
Operators | Description | Expression |
---|---|---|
@distinctUnionOfArrays |
获取数组中 transaction.payee 的值,结果去重 | NSArray *collectedDistinctPayees = [arrayOfArrays valueForKeyPath:@"@distinctUnionOfArrays.payee"]; |
@unionOfArrays |
获取数组中 transaction.payee 的值 | NSArray *collectedPayees = [arrayOfArrays valueForKeyPath:@"@unionOfArrays.payee"]; |
@distinctUnionOfSets |
获取集合中 transaction.payee 的值 | NSSet 嵌套 |
4. Representing Non-Object Values
NSNumber 标量类型
Operators | Description | Expression |
---|---|---|
BOOL | numberWithBool: |
boolValue (in iOS) charValue (in macOS)* |
char | numberWithChar: |
charValue |
double | numberWithDouble: |
doubleValue |
float | numberWithDouble: |
floatValue |
int | numberWithInt: |
intValue |
long | numberWithLong: |
longValue |
long long | numberWithLongLong: |
longLongValue |
short | numberWithShort: |
shortValue |
unsigned char | numberWithUnsignedChar: |
unsignedChar |
unsigned int | numberWithUnsignedInt: |
unsignedInt |
unsigned long | numberWithUnsignedLong: |
unsignedLong |
unsigned long long | numberWithUnsignedLongLong: |
unsignedLongLong |
unsigned short | numberWithUnsignedShort: |
unsignedShort |
NSValue 结构体类型
Operators | Description | Expression |
---|---|---|
NSPoint | valueWithPoint: |
pointValue |
NSRange | valueWithRange: |
rangeValue |
NSRect | valueWithRect: (macOS only). |
rectValue |
NSSize | valueWithSize: |
sizeValue |
ThreeFloats floats = {1., 2., 3.};
NSValue* value = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
[myClass setValue:value forKey:@"threeFloats"];
5. Validating Properties
validateValue:forKey:error:
(或者 validateValue:forKeyPath:error:
)
验证会出现三种结果:
- 验证 Value 对 Key 标识的属性有效,return YES
- 验证 Value 对 Key 标识的属性无效,并不对 Key 进行更改,return NO;并设置错误参数 NSError
- 验证 Value 对 Key 标识的属性无效,但创建了一个有效对象作为替换,return YES;错误对象保持不变;在返回之前,该方法会修改值引用以指向新的值对象。即使值对象是可变的也始终会创建一个新对象
Person* person = [[Person alloc] init];
NSError* error;
NSString* name = @"John";
if (![person validateValue:&name forKey:@"name" error:&error]) {
NSLog(@"%@",error);
}
仅在 Objective-C 中使用
6. Accessor Search Patterns
>> Search Pattern for the Basic Getter
valueForKey:
- 通过以下方法获取实例
get<Key>
,<key>
,is<Key>
,_key
- 通过
countOf<Key>
加上objectIn<Key>AtIndex:
或<key>AtIndexes
其中一个 - 通过三个方法一起获取实例
countOf<Key>
、enumeratorOf<Key>
、memberOf<Key>
-
accessInstanceVariablesDirectly
if return YES, 通过_<key>
、_is<Key>
、<key>
、is<Key>
找到实例变量 - 如果上述都找不到,调用
valueForUndefinedKey:
;如果其中一个找到,结果通过 NSValue、NSNumber、Object 输出
>> Search Pattern for the Basic Setter
setValue:forKey:
- 调用
set<Key>
or_set<Key>
-
accessInstanceVariablesDirectly
if return YES,通过_<key>
、_is<Key>
、<key>
、is<Key>
找到实例变量,并通过变量进行赋值 - 如果上述都找不到,调用
setValue:forUndefinedKey:
>> Search Pattern for Mutable Arrays
mutableArrayValueForKey:
- 寻找
insertObject:in<Key>AtIndex:
+removeObjectFrom<Key>AtIndex:
orinsert<Key>:atIndexes:
+remove<Key>AtIndexes:
方法
如果能找到,则replaceObjectIn<Key>AtIndex:withObject:
orreplace<Key>AtIndexes:with<Key>
方法也可使用 - 如果可变方法找不到,调用
set<Key>
-
accessInstanceVariablesDirectly
if return YES,通过_<key>
、<key>
找到实例变量 - 如果上述都找不到,调用
valueForUndefinedKey:
>> Search Pattern for Mutable Ordered Sets
mutableOrderedSetValueForKey:
- 寻找
insertObject:in<Key>AtIndex:
+removeObjectFrom<Key>AtIndex:
orinsert<Key>:atIndexes:
+remove<Key>AtIndexes:
方法
如果能找到,则replaceObjectIn<Key>AtIndex:withObject:
orreplace<Key>AtIndexes:with<Key>:
方法也可使用 - 如果可变方法找不到,调用
set<Key>
-
accessInstanceVariablesDirectly
if return YES,_<key>
和<key>
- 如果上述都失败,调用
setValue:forUndefinedKey:
>> Search Pattern for Mutable Sets
- 寻找
add<Key>Object:
+remove<Key>Object
oradd<Key>
+remove<Key>
方法 - 如果能找到,则
intersect<Key>:
orset<Key>
方法也可使用 if a managed object
- 如果可变方法找不到,调用
set<Key>
-
accessInstanceVariablesDirectly
if return YES,_<key>
和<key>
- 如果上述都找不到,调用
setValue:forUndefinedKey:
二、Key-Value Coding with Swift
在 Swift 中所有属性都是对象类型,将不会对非对象属性进行处理
三、Other Cocoa Technologies Rely on Key-Value Coding
- Key-value observing
- Cocoa bindings
- Core Data
- AppleScript