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... 把对应的值传出去通知监听者发生了事情