【iOS】KVC 与 KVO 的基本了解与使用

文章目录

  • 前言
  • [一、KVC(Key-Value Coding)键值编码](#一、KVC(Key-Value Coding)键值编码)
    • 概念理解
    • 四个常用方法
    • [key 与 keyPath 区别](#key 与 keyPath 区别)
    • [KVC 查找与赋值流程](#KVC 查找与赋值流程)
      • [(1)查找 set 方法](#(1)查找 set 方法)
      • [(2)未找到 set 方法时](#(2)未找到 set 方法时)
      • (3)如果都没找到
      • [(4)特殊情况:给基本类型赋 nil](#(4)特殊情况:给基本类型赋 nil)
    • [KVC 支持数值与结构体类型](#KVC 支持数值与结构体类型)
    • [KVC 验证机制](#KVC 验证机制)
    • [KVC 对集合的强大支持](#KVC 对集合的强大支持)
    • [KVC 操作字典](#KVC 操作字典)
    • [KVC 的典型应用场景](#KVC 的典型应用场景)
  • [二、KVO(Key-Value Observing)键值观察](#二、KVO(Key-Value Observing)键值观察)
  • [三、KVC 与 KVO 的关系](#三、KVC 与 KVO 的关系)
  • 四、总结

前言

在 iOS 开发中,我们常用 Block、Delegate、通知等方式进行数据传递,这些传值方式在我前面博客都予以介绍过【iOS】多界面传值(五大传值方式)

但其实,Objective-C 还有一对我没有提到过的非常重要的两个东西:KVC(键值编码) 和 KVO(键值观察)

简单介绍的话就是

  • KVC 允许我们通过字符串(key)访问属性;
  • KVO 允许我们自动监听属性值的变化。

它们一个负责"访问",一个负责"观察",是 Cocoa 动态机制的核心。

Cocoa即objective-c框架体系,如UIKit,Foundation,Appkit等

动态机制:程序的某些行为不是在编译时就固定好的,而是在运行时才决定的,总结如下:

  1. 变量在运行时才确定其真实类型
  2. 方法调用在运行时才决定调用哪个实现
  3. 类,方法甚至代码可以在运行时被加载或替换(kvo动态创建子类)

一、KVC(Key-Value Coding)键值编码

概念理解

KVC 是一种通过字符串键名来访问对象属性的机制,而不是直接调用 getter / setter。

例如我们平时这样写:

objc 复制代码
person.name = @"Tom";
NSLog(@"%@", person.name);

使用 KVC 可以写成:

objc 复制代码
[person setValue:@"Tom" forKey:@"name"];
NSLog(@"%@", [person valueForKey:@"name"]);

用字符串当作"钥匙"访问属性,无需明确调用方法。

KVC 是基于 NSObject 的一个扩展类别 ------ NSKeyValueCoding 实现的。

因此,只要类继承自 NSObject,就可以天然支持 KVC。

四个常用方法

方法 作用
setValue:forKey: 通过 key 给属性赋值
setValue:forKeyPath: 通过 keyPath给属性赋值
valueForKey: 通过 key 获取属性
valueForKeyPath: 通过 keyPath 获取属性

key 与 keyPath 区别

objc 复制代码
// key:只能访问当前对象的属性
[coffee setValue:@"100" forKey:@"price"];

// keyPath:可访问关系链属性(例如嵌套对象)
[coffee setValue:@"white" forKeyPath:@"sonOfCoffee.color"];

keyPath 用点号(.)连接多个层级,是对嵌套对象的访问。

KVC 查找与赋值流程

KVC 的底层逻辑非常精细,赋值流程如下:

(1)查找 set 方法

按顺序查找以下方法:

  1. set:
  2. _set:

找到就直接调用

(2)未找到 set 方法时

会检查:

objc 复制代码
+ (BOOL)accessInstanceVariablesDirectly

如果返回 YES(默认值),则会依次查找成员变量:

objc 复制代码
_key → _isKey → key → isKey

找到后直接赋值。

(3)如果都没找到

调用:

objc 复制代码
- (void)setValue:(id)value forUndefinedKey:(NSString *)key

系统默认会抛异常,但你可以重写它,避免崩溃:

objc 复制代码
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"未定义的Key:%@", key);
}

(4)特殊情况:给基本类型赋 nil

若 int、float 等类型被赋 nil,系统会调用:

objc 复制代码
- (void)setNilValueForKey:(NSString *)key

可重写该方法来安全处理:

objc 复制代码
- (void)setNilValueForKey:(NSString *)key {
    if ([key isEqualToString:@"age"]) age = 0;
}

KVC 支持数值与结构体类型

KVC 的 valueForKey: 总是返回 id 类型的对象。

如果访问的是数值或结构体,系统会自动封装成:

  • 数值类 → NSNumber
  • 结构体类 → NSValue

例如:

objc 复制代码
NSNumber *num = [NSNumber numberWithInt:10];
CGPoint point = CGPointMake(54, 45);
NSValue *value = [NSValue valueWithCGPoint:point];

通过 @encode() 还能让自定义结构体被封装:

objc 复制代码
NSValue *v = [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];

KVC 验证机制

KVC 还支持验证赋值是否合法:

objc 复制代码
- (BOOL)validateValue:(inout id *)ioValue
               forKey:(NSString *)inKey
                error:(NSError **)outError {
    if ([inKey isEqualToString:@"name"]) {
        NSString *name = *ioValue;
        if (name.length == 0) {
            *outError = [NSError errorWithDomain:@"MyDomain"
                                            code:1001
                                        userInfo:@{NSLocalizedDescriptionKey:@"名字不能为空"}];
            return NO;
        }
    }
    return YES;
}

KVC 对集合的强大支持

KVC 内置了许多集合运算符,可以直接在数组上操作对象属性。

基本运算符:

运算符 含义
@count 元素数量
@sum 求和
@avg 平均值
@max 最大值
@min 最小值
objc 复制代码
NSNumber *avg = [array valueForKeyPath:@"@avg.age"];
NSNumber *sum = [array valueForKeyPath:@"@sum.age"];

对象运算符:

运算符 作用
@unionOfObjects 不去重
@distinctUnionOfObjects 去重
@unionOfArrays 二维数组不去重
@distinctUnionOfArrays 二维数组去重

示例:

objc 复制代码
NSArray *names = [array valueForKeyPath:@"@unionOfObjects.name"];

KVC 操作字典

KVC 可以轻松在对象和字典之间转换:

objc 复制代码
// 取值为字典
NSDictionary *dict = [user dictionaryWithValuesForKeys:@[@"name", @"age"]];

// 用字典批量赋值
[user setValuesForKeysWithDictionary:@{@"name":@"Tom", @"age":@18}];

这在 Model <-> JSON 转换 中极为常见。

KVC 的典型应用场景

场景 说明
动态访问属性 运行时通过字符串操作属性
访问私有变量 可用于调试或框架内部处理
Model-字典转换 封装与解析 JSON
修改 UI 控件内部属性 如 setValue:forKey:@"_placeholderLabel.textColor"
集合操作 对模型数组进行聚合运算

二、KVO(Key-Value Observing)键值观察

概念回顾

KVO 是一种机制,让一个对象能在另一个对象属性变化时收到通知。

它常用于:

  • Model → Controller 数据同步;
  • Controller → View 自动更新;
  • Model 自身监听依赖属性变化。

实现步骤

objc 复制代码
// 注册观察
[account addObserver:self
          forKeyPath:@"balance"
             options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
             context:nil];

// 回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    NSLog(@"余额变化:%@", change);
}

// 移除观察
[account removeObserver:self forKeyPath:@"balance"];

监听机制(内部原理)

官方文档中的解释:NSObject 在运行时会动态创建一个隐藏的子类,并重写被观察属性的 setter 方法来插入通知逻辑。

流程如下:

  1. 系统创建类:NSKVONotifying_Account
  2. 替换对象的 isa 指针
  3. 重写 setter:
objc 复制代码
willChangeValueForKey:
[super setBalance:newValue];
didChangeValueForKey:
  1. 调用观察者回调

KVO 与通知的区别

对比项 KVO NSNotification
触发时机 属性值改变 手动发布
是否自动触发 ✅ ❌
通知范围 一对一 一对多(全局)
基础实现 依赖 KVC 独立机制
用途 监听对象属性 广播事件消息

KVO 的优缺点

优点 缺点
系统内置,几乎零代码 语法繁琐
自动通知机制 忘记移除崩溃
可观察依赖属性 可读性差
可用于数据绑定 难调试

三、KVC 与 KVO 的关系

可以这样理解:

对比 KVC KVO
作用 动态访问属性 动态监听属性
触发方式 调用 setter/getter setter 内部触发通知
实现依赖 NSObject(NSKeyValueCoding) NSObject(NSKeyValueObserving)
内部关系 提供属性访问通道 基于 KVC 的通道检测变化

KVO 是建立在 KVC 基础上的,所以也可以说没有 KVC,就没有 KVO。

四、总结

概念 特点 典型用途
KVC 用字符串访问属性,支持私有变量和集合操作 JSON 转 Model、调试
KVO 自动监听属性变化并触发通知 数据绑定、界面更新
联系 KVO 内部依赖 KVC 的访问机制 ------

最后一句话概述就是,KVC 打开属性的大门,而当属性被改变时,KVO 会自动提醒我们

相关推荐
风雨_30 分钟前
Mac M3 使用 OrbStack 全新安装运行 EasyConnect 完整教程
ios
Khalil三省1 小时前
Mac怎么显示桌面歌词?Apple Music和Spotify也能开
macos·开源·效率工具·音乐
sakiko_3 小时前
纯Appdelegate控制的老项目适配Xcode27(iOS27SDK)
macos·objective-c·cocoa
Coder_张三20 小时前
iOS 26 适配
前端·ios·swift
用户380341658829721 小时前
崩溃率从 0.3% 到 0.04%:我们做对的不是修 bug,是让每个崩溃只发生一次
ios·swift
2501_915921431 天前
详细教程:使用Perfdog工具测量iOS应用的功耗性能与优化方法
android·macos·ios·小程序·uni-app·cocoa·iphone
风雨_1 天前
RTK (Rust Token Killer) 安装、使用与卸载指南
ios
茶底世界之下1 天前
夜景暗部要提亮多少?用 Awaking 给雪夜街道留一点层次
ios
黑科技iOS上架1 天前
cocos游戏ios上架前加固流程(小蟹iOS混淆)
ios·审核·ios混淆·执行程序差异化