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:
查找 setter 方法
系统首先尝试调用符合 KVC 命名规范的 setter:set<Key>:(首字母大写),例如setName:如果找不到,继续下一步
查找可访问的实例变量(ivar)
如果没有 setter,且类不是@dynamic属性,则 KVC 会尝试直接访问与 key 同名的实例变量(如_name或name),前提是该 ivar 是可访问的(非 private 或被 runtime 允许访问)。调用
setValue:forUndefinedKey:
如果以上都失败,系统会调用setValue:forUndefinedKey:方法。默认实现会抛出NSUnknownKeyException异常。
⚠️ 注意:KVC 可以绕过属性的访问控制(如 private),但前提是运行时能访问到 ivar(通常在继承 NSObject 的类中可行)。
三、KVC 取值过程(valueForKey:)
当调用 valueForKey: 时,系统按以下顺序查找:
查找 getter 方法
依次尝试:get<Key>(如getName)<key>(如name)is<Key>(仅用于 BOOL 类型,如isFinished)_key(如_name)
查找同名实例变量
如果没找到方法,KVC 会尝试直接读取名为_key或key的实例变量。调用
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 框架中“约定优于配置”思想的典型体现。