KVC的本质是什么?

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。


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的关键方法。
复制代码

猜你喜欢

转载自juejin.im/post/7018568536897355790
今日推荐