笔记(二):KVO

2018-07-22  本文已影响12人  RBNote
你以为的并不是你以为的
1-1KVO原理.png

一 基本使用

KVO: key-value observing

//Person 
@interface Person : NSObject
@property (nonatomic, assign) int age;
@end

implementation Person
@end

//VC
#import "ViewController.h"
#import "Person.h"

@interface ViewController ()
@property (nonatomic, strong) Person *person1;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    self.person1 = [[Person alloc] init];
    self.person1.age = 1;
   
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
    
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    self.person2.age = 20;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"监听到:%@的%@:属性的值发生了改变:%@",keyPath,object,change);
}


//移除监听。
- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
}

二 KVO本质

利用Runtime动态生成一个子类 NSKVONotify_xxxxxx
重写子类的setter方法。

从两个方法可以证明对一个对象添加KVO监听之后,KVO内部实现会创建一个新的子类NSKVONotify_xxxxx

eg1:自己创建一个继承自Person的子类  NSKVONotify_Person 什么不做。运行程序。控制他打印如下信息:KVO不起作用了。
KVO failed to allocate class pair for name NSKVONotifying_Person, automatic key-value observing will not work for this class
1-2-KVONotWorkForThisClass.png
eg2:打印一下实例对象的isa指针
//KVO监听的实例对象的isa指针
p self.person1.isa 
(Class) $0 = NSKVONotifying_Person

//未进行KVO监听的实例对象的isa指针 
p self.person2->isa
(Class) $7 = Person
1-3添加KVO监听之后的isa.png

2-1猜测KVO内部实现

苹果不开源,只能靠猜。

kvo 实现原理,给Person对象添加KVO监听为例
1.利用runtime函数动态创建一个继承制Person的子类NSKVONotifying_Person。
2.重写setter方法。(NSKVONotifying_Person 重写了父类Person的setAag:方法 ,
在setter方法内部调用Foundation框架的 _NSSetIntValueAndNotify() 这个函数
3.这个函数 内部做了以下操作
3-1  调用 willChangeValueForKey:方法 ✅ 
3-2 调用父类的setter方法。
3-3 调用 didChangeValueForKey: 方法  ✅
3-3-1 该方法内部调用了 observeValueForKeyPath: ofObject: change: context: 通知监听器,xxxx属性值发生了改变。
//伪代码 
@implementation NSKVONotifying_Person
- (void)setAge:(int)age {
    _NSSetIntValueAndNotify();
}

//伪代码
void _NSSetIntValueAndNotify()
{
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}
- (void)didChnageValueForKey:(NSString *)key {

    //通知监听器,某某属性值发生了改变。
    [observer observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
@end

用图形来描述就是:


1-4NSKVONotifying的内部实现.png

关于 _NSSetIntValueAndNotify() 函数,可以使用runtime提供的函数 && LLDB指令来查看。


1-5关于_NSSetIntValueAndNotify.png

三 题

1.kvo 的实现原理?(本质?)

1.使用runtime动态生成一个子类 NSKVONotifying_xxxxx ,并让instance对象的isa指向这个全新的子类。
2.该子类重写了父类的setter方法,当修改instance对象的属性时,会调用Foundation框架的 _NSSetXXXXValueAndNotify() 函数,该函数内部做了以下操作
2-1.调用 willChangeValueForKey:
2-2 调用父类的 setter方法
2-3 调用 didChangeValueForKey: ✅ 
2-3-1:didChangeValueForKey  ✅方法内部会触发监听器的(observer) 监听方法( observeValueForKeyPath:ofObject:change:context)

2.手动触发KVO?

手动调用 这两个方法
willChangeValueForKey: ✅
didChangeValueForKey: ✅

3.修改成员变量会触发KVO?

NO

示例代码 LowLayerTheoryNote

上一篇下一篇

猜你喜欢

热点阅读