【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 会自动提醒我们

相关推荐
2501_916007474 小时前
iOS 混淆实战,多工具组合完成 IPA 混淆、加固与发布治理(iOS混淆|IPA加固|无源码混淆|App 防反编译)
android·ios·小程序·https·uni-app·iphone·webview
2501_915918414 小时前
怎么上架 App?iOS 应用上架完整流程详解与跨平台发布实战指南
android·ios·小程序·https·uni-app·iphone·webview
马拉萨的春天4 小时前
谈谈你对iOS的runtime和runloop的了解
macos·ios·cocoa
开开心心loky4 小时前
[iOS] 计算器仿写
ios
2501_929157684 小时前
【安卓+PC+IOS】psp全中文游戏+高清纹理包+金手指
android·游戏·ios
2501_916008894 小时前
iOS 混淆工具链实战 多工具组合完成 IPA 混淆与加固(iOS混淆|IPA加固|无源码加固|App 防反编译)
android·ios·小程序·https·uni-app·iphone·webview
xingxing_F4 小时前
A Better Finder Rename for mac 文件批量重命名
macos
0wioiw04 小时前
RealVNC Viewer(Windows控制Mac)
macos
自在极意功。8 小时前
贪心算法深度解析:从理论到实战的完整指南
java·算法·ios·贪心算法