KVC的基本原理

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"];

常见的集合操作符:

  1. 聚合操作符
  • @avg
  • @count
  • @max
  • @min
  • @sum
  1. 数组操作符
  • @distinctUnionOfObjects
  • @unionOfObjects

注意事项

  1. 性能考虑
  • KVC的性能比直接访问属性要低,因为它需要进行字符串比较和方法查找
  1. 类型安全
  • KVC是非类型安全的,运行时才会发现错误
  • 使用时需要注意值的类型转换
  1. 异常处理
// 处理未定义的key
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"Undefined key: %@", key);
}

- (id)valueForUndefinedKey:(NSString *)key {
    NSLog(@"Undefined key: %@", key);
    return nil;
}
  1. 空值处理
// 处理nil值
- (void)setNilValueForKey:(NSString *)key {
    if ([key isEqualToString:@"age"]) {
        [self setValue:@0 forKey:key];
    } else {
        [super setNilValueForKey:key];
    }
}

KVC是iOS开发中一个非常强大的特性,它为我们提供了一种通过字符串来访问对象属性的方式,在某些场景下特别有用,比如:

  • 动态设置UI控件的属性
  • 字典转模型
  • 操作私有属性
  • 集合操作

但使用时需要注意类型安全和性能问题。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容