KVC 崩溃(Crash)的根源主要集中在以下四个维度:
1. 找不到指定的 Key (Undefined Key)
这是最常见的崩溃原因。当你尝试访问一个对象中不存在的方法或成员变量时,KVC 找不到任何接入点。
- 根源:
setValue:forKey:或valueForKey:搜索完所有路径(Setter/Getter/Ivar)后仍一无所获。 - 触发方法: 默认调用
-setValue:forUndefinedKey:或-valueForUndefinedKey:,这两个方法的默认实现是抛出NSUndefinedKeyException异常。 - 常见场景: 拼写错误、服务器下发的字段名在模型中没有对应定义。
2. 给非对象属性设为 nil (Illegal Nil Value)
如果你尝试通过 KVC 给一个基本数据类型(如 int, float, BOOL, struct 等)赋值为 nil,程序会立即崩溃。
- 根源: 基本数据类型不能接受
nil。 - 触发方法: 默认调用
-setNilValueForKey:,其默认实现是抛出NSInvalidArgumentException。 - 解决: 如果你的模型中有
int age,你应该确保 KVC 赋值时传入的是[NSNumber numberWithInt:0]而非nil。
3. Key Path 路径断裂
当你使用 setValue:forKeyPath: 这种级联访问时,如果路径中间的某个节点为 nil,后续的操作就可能引发崩溃。
- 例子:
[person setValue:@"Beijing" forKeyPath:@"address.city"]。 - 根源: 如果
person.address为nil,KVC 在尝试进一步寻找city时会因为“给 nil 发送 valueForKey:”或由于对象查找失败而导致逻辑错误或崩溃。
4. 类型不匹配 (Type Mismatch)
虽然 KVC 会尝试自动装箱/拆箱(如 NSNumber 转 int),但它不能处理完全无关的类型转换。
- 根源: 传入的
value对象无法转换为目标属性所需的类型。 - 常见场景: 尝试把一个
NSString赋值给一个需要UIImage的属性。虽然有些场景下不会直接崩溃(而是静默失败或在后续业务代码中因类型错误崩溃),但在强类型转换或结构体转换中极易出问题。
5. 防御方案:如何避免这些崩溃?
为了写出健壮的 KVC 代码,建议采取以下防御措施:
| 异常类型 | 防御手段 |
|---|---|
| Undefined Key | 重写 setValue:forUndefinedKey:,即使什么都不做也能防止崩溃。 |
| Nil Value | 重写 setNilValueForKey:,在内部给变量赋一个默认值(如 0)。 |
| 拼写错误 | 使用 NSStringFromSelector(@selector(propertyName)) 代替纯硬编码字符串。 |
| 路径风险 | 在执行 KeyPath 之前,先确保中间环节的对象已经初始化。 |
一个容易被忽略的细节:KVO 关联崩溃
有时候 KVC 崩溃并不是因为 KVC 本身,而是因为你正在监听该属性。当你通过 setValue:forKey: 修改属性时,会触发 KVO 观察者。如果观察者已经被销毁但没有移除,或者在回调方法里发生了数组越界等逻辑错误,崩溃的堆栈往往会显示在 KVC 这一层。