KVC,KVO详解

KVO(键值监听)

作用:用于做属性监听,也可以利用属性set方法在内部监听

实现原理

  • KVO是基于runtime机制实现的
  • 当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
  • 派生类在被重写的 setter 方法实现真正的通知机制(Personà NSKVONotifying_Person)

实例

  • 1.2.1 替属性注册监听
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)KVO
{
Person *p = [[Person alloc] init];
p.age = 30;

// 注册属性监听
[p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];

p.age = 30;
p.age = 40;

self.p = p;
}
  • 1.2.2 释放监听器

    两种方式删除指定key路径的监听器

    • removeObserver: forKeyPath
    • removeObserver: forKeyPath: context:
1
2
3
4
- (void)dealloc
{
[self.p removeObserver:self forKeyPath:@"age"];
}
  • 1.2.3 回调:当被监听的属性的值改变时调用
1
2
3
4
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@对象的%@属性改变了:%@", object, keyPath, change);
}

KVC(键值编码)

  • 1.可以访问私有成员变量的值
  • 2.可以间接修改私有成员变量的值(替换系统自带的导航栏、tabbar)

访问属性

公有属性

私有属性

1
2
3
4
5
6
Person *p = [[Person alloc] init];
p.age = 20;
// 访问公有属性
int age = [[p valueForKey:@"age"] intValue];
// 访问私有属性
NSLog(@"%f", [[p valueForKey:@"height"] floatValue]);

修改私有属性(readonly)

1
2
3
4
5
[pageControl setValue:[UIImage
imageNamed:@"compose_keyboard_dot_normal"] forKeyPath:@"pageImage"];
[pageControl setValue:[UIImage
imageNamed:@"compose_keyboard_dot_selected"] forKeyPath:@"currentPageImage"];
[self addSubview:pageControl];
  • forKey和forKeyPath区别

keyPath包含了key的功能

key:只能访问当前对象的属性

keyPath:能利用.运算符一层一层往内部访问属性

1
2
3
4
5
6
7
8
9
// 两者都可以访问属性
float h1 = [[p valueForKey:@"height"] floatValue];
float h2 = [[p valueForKeyPath:@"height"] floatValue];
NSLog(@"%f %f", h1, h2);

p.dog = [[HMDog alloc] init];
p.dog.name = @"wangcai";
// forKeyPath可以根据路径访问属性
NSLog(@"%@", [p valueForKeyPath:@"dog.name"]);

其他

计算数组长度,元素和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Book *b3 = [[Book alloc] init];
b3.name = @"aa";
b3.price = 12;

p.books = @[b1, b2, b3];

// 计算数组的长度
NSLog(@"%@", [p valueForKeyPath:@"books.@count"]);

// 取出对象数组中某一元素的值,形成新的数组
NSArray *names = [p valueForKeyPath:@"books.name"];
NSArray *names = [p.books valueForKeyPath:@"name"];
NSLog(@"%@", names);

// 计算数组中某一个元素的和
double sumPrice = [[p valueForKeyPath:@"books.@sum.price"] doubleValue];
NSLog(@"%f", sumPrice);