johnhao
johnhao
发布于 2017-07-04 / 1 阅读
0

iOS-KVC

KVC(Key-Value Coding,键值编码)是 Objective-C 和 Swift(通过 Foundation 框架)中一种访问对象属性的机制,它允许你通过字符串形式的 key(或 keyPath)来间接访问对象的属性,而不需要直接调用 getter/setter 方法。


一、KVC 的基本使用

在 Objective-C 中:

// 赋值
[person setValue:@"Alice" forKey:@"name"];

// 取值
NSString *name = [person valueForKey:@"name"];

在 Swift 中(需继承自 NSObject):

person.setValue("Alice", forKey: "name")
let name = person.value(forKey: "name") as? String

二、KVC 赋值过程(setValue:forKey:)

当调用 setValue:forKey: 时,系统会按以下顺序查找并执行 setter:

  1. 查找 setter 方法
    系统首先尝试调用符合 KVC 命名规范的 setter:

    • set<Key>:(首字母大写),例如 setName:

    • 如果找不到,继续下一步

  2. 查找可访问的实例变量(ivar)
    如果没有 setter,且类不是 @dynamic 属性,则 KVC 会尝试直接访问与 key 同名的实例变量(如 _namename),前提是该 ivar 是可访问的(非 private 或被 runtime 允许访问)。

  3. 调用 setValue:forUndefinedKey:
    如果以上都失败,系统会调用 setValue:forUndefinedKey: 方法。默认实现会抛出 NSUnknownKeyException 异常。

⚠️ 注意:KVC 可以绕过属性的访问控制(如 private),但前提是运行时能访问到 ivar(通常在继承 NSObject 的类中可行)。


三、KVC 取值过程(valueForKey:)

当调用 valueForKey: 时,系统按以下顺序查找:

  1. 查找 getter 方法
    依次尝试:

    • get<Key>(如 getName

    • <key>(如 name

    • is<Key>(仅用于 BOOL 类型,如 isFinished

    • _key(如 _name

  2. 查找同名实例变量
    如果没找到方法,KVC 会尝试直接读取名为 _keykey 的实例变量。

  3. 调用 valueForUndefinedKey:
    若仍找不到,调用 valueForUndefinedKey:,默认抛出异常。


四、KVC 的底层原理

KVC 是基于 Objective-C 的 Runtime 机制 实现的:

  • 利用 objc_msgSend 动态发送消息(即动态调用方法)

  • 使用 runtime 函数(如 class_getInstanceVariable, object_setIvar)直接操作实例变量

  • 支持 keyPath(如 person.address.city),通过递归解析路径逐级调用 KVC

例如,valueForKeyPath:@"address.city" 会被拆解为:

[[person valueForKey:@"address"] valueForKey:@"city"]

五、注意事项

  • 类型处理:KVC 要求对象类型。对于基本数据类型(如 int、BOOL),会自动包装/解包为 NSNumber。

  • nil 处理:向 KVC 传入 nil 值时,会调用 setNilValueForKey:,默认抛异常(可重写以自定义行为)。

  • 性能:KVC 比直接方法调用慢,因为涉及字符串查找和消息转发。

  • 安全性:使用字符串作为 key 容易出错(拼写错误导致崩溃),建议配合 #keyPath()(Swift)或宏(Objective-C)提高安全性。


六、应用场景

  • 数据模型与 UI 绑定(如早期 Cocoa Bindings)

  • 字典转模型(如 setValuesForKeys(dict)

  • 动态配置对象属性

  • 配合 KVO(Key-Value Observing)实现观察者模式


总结:KVC 是一种基于 Runtime 的动态属性访问机制,通过方法查找 + 实例变量访问 + 异常回调 的流程实现灵活的对象属性操作,是 Cocoa 框架中“约定优于配置”思想的典型体现。