1.首先了解什么是KVO
KVO 即Key-Value Observing ,他提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知,简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知
相应的观察者了.
下面举个例子进行说明
1.创建一个Person类 继承自NSObject,它有Name,height,age 三个属性
.h文件
#import <UIKit/UIKit.h>
@interface Person : NSObject
@property (nonatomic, copy)NSString *name;
@property (nonatomic, assign) CGFloat height;
@property (nonatomic, assign) NSInteger age;
@end
.m文件 在.m文件中重写height的setter方法.
#import "Person.h"
@implementation Person
- (void)setHeight:(CGFloat)height
{
_height = height;
NSLog(@"setHeight");
}
@end
2.在控制器中实例化一个person对象 ,并给它的属性赋值
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc]init];
self.person = person;
person.name = @"xiaoming";
person.age = 19;
person.height = 180;
//给person 添加一个监听者,监听它的属性 height的改变
(注:其中forKey 和Keypath的区别是,当使用forkey的时候,只会在本类中寻找,如果没有就崩溃,如果使用keypath也会在它的子类中寻找)
[person addObserver:self forKeyPath:@"height" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"aa"];
person.height = 178;
}
//当键值发生改变的时候就会调用
//参数3 change 修改之后的值 或者 旧值 参数4context 附加参数
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"change = %@",change);
}
/////////////////////////////////////
打印输出的结果
2016-04-25 15:50:19.541 09-KVO底层实现[1147:56293] change = {
kind = 1;
new = 178;
old = 180;
}
)
下面说一下KVO的底层实现
在添加KVO的时候 在运行时会添加一个当前类的子类(NSKVONotifying_Person),并重写子类的set方法,在添加监听后更改了isa指针 (isa 指针的而作用:
调用一个类的方法 会根据 isa指针去对应的方法列表中找这个方法然后找到这个方法的实现 然后执行)把它指向了子类,当再次赋值的时候会调用子类的set方法.
总结:
当使用KVO时
1.动态的创建一个叫NSKVONotifying_CZPerson 的子类
2.更改当前类的 isa指针为子类
3.传入一堆参数 1.监听者(将来调用observeValueForKeyPath) 2.keypath(决定了重写哪个set方法) 3.枚举(决定传哪些给你) 4.携带参数
4.根据keypath 重写子类的set方法
5.在子类的set方法中 根据枚举 保存所有的属性值 然后调用父类的set方法 然后调用监听者的observeValueForKeyPath... 把对应的值传出去通知监听者发生了事情