KVC是通过NSKeyValueCoding非正式协议实现的,主要用于间接访问对象的属性。其核心方法是setValue:forKey:和valueForKey:。
实现原理
1. setValue:forKey: 的搜索顺序:
- (void)setValue:(id)value forKey:(NSString *)key {
// 1. 先查找set<Key>:方法
if ([self respondsToSelector:@"set<Key>:"]) {
[self "set<Key>:"]
return;
}
// 2. 查找_set<Key>:方法
if ([self respondsToSelector:@"_set<Key>:"]) {
[self "_set<Key>:"]
return;
}
// 3. 检查是否可以直接访问实例变量
if (accessInstanceVariablesDirectly) {
// 按照以下顺序查找成员变量
// _<key>
// _is<Key>
// <key>
// is<Key>
if (找到了相应的实例变量) {
直接赋值;
return;
}
}
// 4. 如果都没找到,调用setValue:forUndefinedKey:
[self setValue:value forUndefinedKey:key];
}
2. valueForKey: 的搜索顺序:
- (id)valueForKey:(NSString *)key {
// 1. 查找get<Key>、<key>、is<Key>方法
if ([self respondsToSelector:@"get<Key>"] ||
[self respondsToSelector:@"<key>"] ||
[self respondsToSelector:@"is<Key>"]) {
return [self "相应的方法"];
}
// 2. 查找_<key>方法
if ([self respondsToSelector:@"_<key>"]) {
return [self "_<key>"];
}
// 3. 检查是否可以直接访问实例变量
if (accessInstanceVariablesDirectly) {
// 按照以下顺序查找成员变量
// _<key>
// _is<Key>
// <key>
// is<Key>
if (找到了相应的实例变量) {
return 实例变量的值;
}
}
// 4. 如果都没找到,调用valueForUndefinedKey:
return [self valueForUndefinedKey:key];
}
实际使用示例
// 定义一个类
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation Person
@end
// 使用KVC
Person *person = [[Person alloc] init];
// 设置值
[person setValue:@"Tom" forKey:@"name"];
[person setValue:@25 forKey:@"age"];
// 获取值
NSString *name = [person valueForKey:@"name"];
NSNumber *age = [person valueForKey:@"age"];
集合操作符
KVC还支持集合操作符,用于数组和集合的操作:
// 假设有一个包含Person对象的数组
NSArray *persons = @[person1, person2, person3];
// 使用集合操作符
NSNumber *avgAge = [persons valueForKeyPath:@"@avg.age"];
NSNumber *sumAge = [persons valueForKeyPath:@"@sum.age"];
NSNumber *maxAge = [persons valueForKeyPath:@"@max.age"];
NSNumber *minAge = [persons valueForKeyPath:@"@min.age"];
NSArray *uniqueNames = [persons valueForKeyPath:@"@distinctUnionOfObjects.name"];
常见的集合操作符:
- 聚合操作符
- @avg
- @count
- @max
- @min
- @sum
- 数组操作符
- @distinctUnionOfObjects
- @unionOfObjects
注意事项
- 性能考虑
- KVC的性能比直接访问属性要低,因为它需要进行字符串比较和方法查找
- 类型安全
- KVC是非类型安全的,运行时才会发现错误
- 使用时需要注意值的类型转换
- 异常处理
// 处理未定义的key
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
NSLog(@"Undefined key: %@", key);
}
- (id)valueForUndefinedKey:(NSString *)key {
NSLog(@"Undefined key: %@", key);
return nil;
}
- 空值处理
// 处理nil值
- (void)setNilValueForKey:(NSString *)key {
if ([key isEqualToString:@"age"]) {
[self setValue:@0 forKey:key];
} else {
[super setNilValueForKey:key];
}
}
KVC是iOS开发中一个非常强大的特性,它为我们提供了一种通过字符串来访问对象属性的方式,在某些场景下特别有用,比如:
- 动态设置UI控件的属性
- 字典转模型
- 操作私有属性
- 集合操作
但使用时需要注意类型安全和性能问题。