当前位置:网站首页>Runtime——KVC,KVO原理
Runtime——KVC,KVO原理
2022-08-08 05:43:00 【chabuduoxs】
KVC是什么?
KVC是Key Value Coding的缩写,意思是键值编码。 在iOS中,提供了一种方法通过使用属性的名称(也就是Key)来间接访问对象属性的方法,这个方法可以不通过getter/setter方法来访问对象的属性。 用KVC可以间接访问对象属性的机制。通常我们使用valueForKey 来替代getter 方法,setValue:forKey来代替setter方法。
常用API:
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
setValue:forKey:和valueForKey:只能用来访问当前对象的属性,而keyPath可以通过当前对象访问属性的属性,可以一层一层套下去。
简单举个例子:
#import <Foundation/Foundation.h>
#import "Car.h"
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) Car* car;
@end
NS_ASSUME_NONNULL_END```
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Car : NSObject
@property (nonatomic, strong) NSString* name;
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Person* person = [[Person alloc] init];
person.car = [[Car alloc] init];
// KVC
[person setValue:@"20" forKey:@"age"];
NSLog(@"%@", [person valueForKey:@"age"]);
[person setValue:@111 forKeyPath:@"car.name"];
NSLog(@"%@", [person valueForKeyPath:@"car.name"]);
}
KVC的底层原理
KVC赋值
keypath的底层和key是同理的,下面分析一个key的流程,上一张网上很明白的图。
下面分析一下步骤:
- 首先第一步,先查找该类的
setter方法,首先会把参数key的首字母大写,如何根据Key去查找对应的setter方法,包括setKey和_setKey,如果找到了就调用这个方法并且传入参数value,从而修改值,如果没有找到setter方法,就回去查看该类的+accessInstanceVariablesDirectly类方法的返回值(能不能直接访问成员变量,默认为YES),如果不能直接访问,就会报错,并且触发“setValue:forUndefinedKey:"方法。 - 如果能访问成员变量,会按照顺序查找对应的成员变量,
_key,_isKey,key,isKey,如果找到了对应的成员变量,就直接给成员变量赋值,(如果设置了KVO的话,此时会触发KVO,因为KVC在此处,也调用了willChangeValueForKey:和didChangeValueForKey:方法),如果没找到成员变量,就会报错,并且触发“setValue:forUndefinedKey:"方法。
KVC取值

- 类似于上面的流程,只不过改为先查找getter方法,按照getKey,key,isKey,_key顺序查找
- 如果没有找到getter方法,就回去查看该类的
+accessInstanceVariablesDirectly类方法的返回值(能不能直接访问成员变量,默认为YES),如果不能直接访问,就会报错,并且触发“valueForUndefinedKey:"方法。 - 如果能访问成员变量,会按照顺序查找对应的成员变量,
_key,_isKey,key,isKey,如果没找到,就会报错,并且触发“valueForUndefinedKey:"方法。
KVO?
KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变。
使用KVO只有三个关键点:被观察是谁:即想要观察哪个对象哪个属性值变化;观察者是谁:谁观察这个对象,确定后就可以给对象添加KVO和移除对象的KVO了;观察者的回调方法:对象属性变化后触发的方法.
举个例子:
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person* person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.person = [[Person alloc] init];
self.person.car = [[Car alloc] init];
// KVO
// 给person添加观察者
[self.person addObserver:self forKeyPath:@"age" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:nil];
// KVC
[self.person setValue:@"20" forKey:@"age"];
NSLog(@"%@", [self.person valueForKey:@"age"]);
[self.person setValue:@111 forKeyPath:@"car.name"];
NSLog(@"%@", [self.person valueForKeyPath:@"car.name"]);
}
- (void)dealloc {
// 移除person对象的KVO
[self.person removeObserver:self forKeyPath:@"age"];
}
// 回调方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"被观察对象%@的%@属性发生变化:%@", object, keyPath, change);
}
@end
具体使用以及参数含义可以参考我之前的博客——简单使用KVO

KVO底层原理
既然是利用了Runtime实现的KVO,就来探索一下,到底是如何实现的。
先下结论。
- 首先在运行时在利用Runtime给这个类新建一个子类,并且将使用了KVO的instance的isa指针指向这个新建的类。此时这个实例对象已经不是原来的类了,而是属于新建的子类。
- 接下来这个类会重写原来类以及NSObject类的部分方法,结合之前KVC找setter方法,我们可以想到最重要的就是重写实例方法setter。
- 重写后的setter主要三个流程:首先调用willChangeValueForKey:表示即将修改值,然后找对象的setter方法:真正去修改成员变量的值,最后调用didChangeValueForKey:并且在这个方法内实现观察者调用观察者回调方法。
可以看看它的大概伪代码:
@interface NSKVONotifying_Person : Person
@end
@implementation NSKVONotifying_Person
- (void)setAge:(int)age
{
_NSSetIntValueAndNotify();
}
// 伪代码
void _NSSetIntValueAndNotify()
{
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
- (void)didChangeValueForKey:(NSString *)key
{
// 通知监听器,某某属性值发生了改变
[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
// 屏幕内部实现,隐藏了NSKVONotifying_MJPerson类的存在
- (Class)class
{
return [Person class];
}
- (void)dealloc
{
// 收尾工作
}
- (BOOL)_isKVOA
{
return YES;
}
@end

边栏推荐
- 卷积神经网络 图像识别,卷积神经网络 图像处理
- tracepoint: 定义函数及调用示例
- 数据库系统原理与应用教程(080)—— MySQL 练习题:操作题 186-193(二十四):综合练习
- Several postman features worth collecting will help you do more with less!
- postman---postman参数化
- cloud computing and cloud services, cloud computing
- 14.Unity2D 横版 粒子系统特效 飙血粒子+高处落地粒子+对象池管理所有粒子
- [Untitled] I haven't thought of a name yet
- Typescript namespace
- Rust development - Struct usage example
猜你喜欢

automation tool

nonebot插件:说话的艺术

卷积神经网络 图像识别,卷积神经网络 图像处理

How to batch import files and rename them all to the same file name

cnn卷积神经网络反向传播,卷积神经网络维度变化

sqlmap+dnslog注入复现

Unity鼠标光标使用学习

棋盘染色问题

Query and track multiple express tracking numbers, and filter the tracking numbers shipped at a certain time

121道分布式面试题和答案
随机推荐
【matlab】matlab中变量赋值函数deal
研发医疗器械产品需要做的测试
KDD‘22推荐系统论文梳理(24篇研究&36篇应用论文)
Checkerboard Coloring Problem
卷积神经网络 图像识别,卷积神经网络 图像处理
仿QQ好友列表,QListWidget!
TSF Microservice Governance Combat Series (2) - Service Routing
tracepoint: 定义函数及调用示例
Go-Excelize API源码阅读(十)—— SetActiveSheet(index int)
Unity mouse cursor usage learning
Go-Excelize API source code reading (10) - SetActiveSheet(index int)
分布式事务 :可靠消息最终一致性方案
IP核之RAM实验
【图像处理】matlab基础图像处理 | 图像载入、图像添加噪声、图像滤波、图像卷积
wpf中DataGrid的样式
TCP/IP基本实现
stack-queue
[Untitled] I haven't thought of a name yet
云计算和云服务,云计算
TSF微服务治理实战系列(二)——服务路由