KVC/KVO 使用细节和调用顺序
Key-Value Coding (KVC)
Key-Value Coding (KVC)
KVC,即是指 NSKeyValueCoding,一个非正式的 Protocol,提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。
一个对象拥有某些属性。比如说,一个 Person 对象有一个 name 和一个 address 属性。以 KVC 说法,Person 对象分别有一个 value 对应他的 name 和 address 的 key。 key 只是一个字符串,它对应的值可以是任意类型的对象。从最基础的层次上看,KVC 有两个方法:一个是设置 key 的值,另一个是获取 key 的值。
// 重写setter方法和getter 方法:
1. Dog对象有属性:
@property(nonatomic,copy)NSString *name;
2. 如果同时重写setter和getter方法:
![](https://app.yinxiang.com/shard/s21/res/16f9902d-f2aa-4a65-9892-01ea8c3ecc29.png)
// 记住: 如果同时重写setter和getter方法,系统不会自动生成_name的成员变量.
需要 @synthesize name = _name;
以告诉系统,你要帮我生成_name
3. [myDog setValue:@"MBXxx" forKey:@"_name"];
@“_name” @“name” 都可以访问到该成员变量
4. NSString *age = [myDog valueForKey:@"age"];
调用这句代码时,
1) 系统先去找该变量有没有getter方法. 如果有,调用getter方法取值
2) 如果没有getter方法,系统会查找有没有age成员或者_age成员.
5. 如果仅仅在.m文件中声明了全局的变量:
{
@public
@public
NSString *age;
}
这时,只能通过KVC模式来赋值和取值.
// 注: 先赋值,再取.
[myDog setValue:@"10" forKey:@"age"];
NSString *age = [myDog valueForKey:@"age"];
NSString *age = [myDog valueForKey:@"age"];
NSLog(@"age ==== %@",age);
打印结果: age === 10
说明,赋值和取值过程成功.
KVO:
即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
// dog.m 代码:
#import "Dog.h"
@implementation Dog
{
@public
{
@public
NSString *age;
}
@synthesize name = _name;
-(void)setName:(NSString *)name{ // 1
NSLog(@"xxxxx");
_name = name;
}
-(NSString *)name{ // 2
NSLog(@"AAAAA");
return _name;
NSLog(@"AAAAA");
return _name;
}
@end
person.m 代码:
#import "Person.h"
@implementation Person
-(void)setDog:(Dog *)dog{ // 3
_dog = dog;
// 添加当前对象为观察者
[_dog addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"hello kvo"];
}
-(void)dealloc{ // 6
[_dog removeObserver:self forKeyPath:@"name"];
@implementation Person
-(void)setDog:(Dog *)dog{ // 3
_dog = dog;
// 添加当前对象为观察者
[_dog addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"hello kvo"];
}
-(void)dealloc{ // 6
[_dog removeObserver:self forKeyPath:@"name"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ // 4
NSLog(@"context==%@",context);
if([object isKindOfClass:[Dog class]] && [keyPath isEqualToString:@"name"]){
NSString *new = change[NSKeyValueChangeNewKey];
NSLog(@"new === %@",new);
}
}
NSLog(@"new === %@",new);
}
}
@end
下面看看调用过程是怎样的?
Person *p = [[Person alloc]init];
p.dog = myDog;
p.dog = myDog;
myDog.name = @"fyz”; //5
1)调用 p.dog = myDog; 方法时,走 方法3.
2)然后来到 方法5
3)来到 方法2(getter) //打印了AAAA
4)然后 来到 方法1(setter) // 打印XXXX (这里为什么?需要测试)
5 ) 又来到 方法2( getter ) //打印了AAAA (这里又为什么?需要测试)
6 ) 然后来到 方法4 // 打印new ===
7 ) 最后走析构函数 方法6
// 没有加入观察者模式时,只会调一次 方法1(set方法),相当于观察者模式的存在.
先调用了一次getter方法, 再调一次setter方法,再调一次getter方法.