小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
KVC全称是Key-Value Coding,
俗称“键值编码”,可以通过一个key来访问某个属性。
常见的API有:
- (void)setValue:(id)value forKeyPath:(NSString*)keyPath;
- (void)setValue:(id)value forKey:(NSString*)key;
- (id)valueForKeyPath:(NSString*)keyPath;
- (id)valueForKey:(NSString*)key;
复制代码
前两个是用来设置值的,后边两个是取值的。
先来一个小demo演示一下KVC基本用法:
创建一个Person类。
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end
@implementation Person
@end
Person *person = [[Person alloc] init];
我们给person某个属性设置值,我们一般用set方法:
person.age = 10;
我们取person 的某个的值,我们一般用get方法:
person.age
实际上,我们也可以用:
[person setValue:@10 forKey:@"age"];
来给person对象的age属性赋值。
可以用:
NSLog(@"%@", [person valueForKey:@"age"]);
来获取person对象的age属性值。
或者用
[person setValue:@10 forKeyPath:@"cat.weight"];
来给person对象的age属性赋值。
NSLog(@"%@", [person valueForKeyPath:@"cat.weight"]);
来获取person对象的age属性值。
那么,
- (void)setValue:(id)value forKeyPath:(NSString*)keyPath;
- (void)setValue:(id)value forKey:(NSString*)key;
这两个函数有什么区别吗?
- (id)valueForKeyPath:(NSString*)keyPath;
- (id)valueForKey:(NSString*)key;
这两个函数又有什么区别吗?
复制代码
接下来我们再看一个demo:
我们创建另外一个类:
@interface Cat : NSObject
@property (assign, nonatomic) int weight;
@end
让Person类有一个cat对象。
@interface Person : NSObject
@property (assign, nonatomic) int age;
@property (assign, nonatomic) Cat *cat;
@end
以前我们想给person对象的cat对象的 weight属性赋值,我们该这样做。
Person *person = [[Person alloc] init];
Cat *cat = [[Cat alloc] init];
cat.weight = 10;
person.cat = cat;
那么现在有了
- (void)setValue:(id)value forKeyPath:(NSString*)keyPath;
方法。我们就可以直接这样设置。
[person setValue:@10 forKeyPath:@"cat.weight"];
但是
- (void)setValue:(id)value forKey:(NSString*)key;
就不可以这样写:
也就是说:
- (void)setValue:(id)value forKeyPath:(NSString*)keyPath;
功能更加强大。
同样:我们访问person对象的cat对象
的weight属性。可以这样写。
NSLog(@"%@", [person valueForKeyPath:@"cat.weight"]);
而。
- (id)valueForKey:(NSString*)key;
这个方法就不行。
更多的使用,自己可以写代码尝试下。
由于KVC也可以实现set方法类似的修改对象的属性。
那么,我们有这样一个疑问。
复制代码
通过KVC修改属性会不会触发KVO?
怎么回答这个问题? 对!就是写代码测试一下。
怎么写测试代码? 一个person类,一个observer类。
让observer对象监听person对象的age属性变化。 注意:这一次改变
扫描二维码关注公众号,回复:
13167620 查看本文章
person对象的age属性时,我们使用KVC的方式修改age属性。并不用setAge:方法。
Observer类:
@interface Observer : NSObject
@end
@implementation Observer
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"observeValueForKeyPath - %@", change);
}
@end
Person类:
@interface Person : NSObject
@property (assign, nonatomic) int age;
@end
测试:
Observer *observer = [[Observer alloc] init];
Person *person = [[Person alloc] init];
// 添加KVO监听
[person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 通过KVC修改age属性
[person setValue:@10 forKey:@"age"];
通过打断点及打印结果如下:
observeValueForKeyPath - {
kind = 1;
new = 10;
old = 0
}
KVC为什么能触发KVO?
我们需要研究 setValue: forKey:方法。
[person setValue:@10 forKey:@"age"];
setValue:forKey:的原理:
![屏幕快照 2021-10-13 下午11.10.45.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8cefbe0dc9db44009a44155efff91f79~tplv-k3u1fbpfcp-watermark.image?)
如何证明:
可以 重写方法,增减成员变量等。来测试。。。。。
@interface Person : NSObject
{
@public
// int age;
// int isAge;
// int _isAge;
// int _age;
}
@implementation Person
//- (void)setAge:(int)age
//{
// NSLog(@"setAge: - %d", age);
//}
//- (void)_setAge:(int)age
//{
// NSLog(@"_setAge: - %d", age);
//}
//- (void)willChangeValueForKey:(NSString *)key
//{
// [super willChangeValueForKey:key];
// NSLog(@"willChangeValueForKey - %@", key);
//}
//
//- (void)didChangeValueForKey:(NSString *)key
//{
// NSLog(@"didChangeValueForKey - begin - %@", key);
// [super didChangeValueForKey:key];
// NSLog(@"didChangeValueForKey - end - %@", key);
//}
// 默认的返回值就是YES
//+ (BOOL)accessInstanceVariablesDirectly
//{
// return YES;
//}
@end
通过KVC修改属性会不会触发KVO?
会触发KVO。
之前的文章我们说直接修改成员变量不能够触发KVO.
Person *person = [[Person alloc] init];
person->_age = 10; // 直接修改成员变量,不能够触发KVO.
那通过KVC直接去修改_age成员变量时,也是可以触发KVO的。这是为什么?
KVC的赋值、取值过程是怎样的?原理是什么?
在Person类中重写
willChangeValueForKey:
didChangeValueForKey:
会发现,通过KVC直接修改age属性时,会调用上面的方法。
//可以猜测: 通过KVC修改age属性:
[person setValue:@10 forKey:@"age"];
赋值过程的内部调用:
[person willChangeValueForKey:@"age"];
person->_age = 10;
[person didChangeValueForKey:@"age"];
接下来看看取值过程的 内部调用:
valueForKey:的原理:
![屏幕快照 2021-10-13 下午11.11.14.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9ea5aa8d27404e628cd7bf8451bd378e~tplv-k3u1fbpfcp-watermark.image?)
如何证明:
可以 重写方法,增减成员变量等。来测试。。。。。
@interface Person : NSObject
{
@public
// int age;
// int isAge;
// int _isAge;
// int _age;
}
@implementation Person
//- (int)getAge
//{
// return 11;
//}
//- (int)age
//{
// return 12;
//}
//- (int)isAge
//{
// return 13;
//}
//- (int)_age
//{
// return 14;
//}
// 默认的返回值就是YES
//+ (BOOL)accessInstanceVariablesDirectly
//{
// return YES;
//}
@end
注意:setKey方法。。。。。。是调用KVO的关键方法。
复制代码