定义1个类父类继承NSObject
@interface A : NSObject
@property (nonatomic, strong) id obj;
@end
@implementation A
- (instancetype)init {
self = [super init];
if (self) {
self.obj = @"defaulf";
}
return self;
}
@end
定义一个子类继承A
@interface B : A
@end
@implementation B
- (void)setObj:(id)obj {
super.obj = obj;
NSLog(@"子类来了");
}
@end
在控制器中对子类B进行监听obj的改变
@interface ViewController ()
@property(nonatomic, strong) B *b;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.b = [[B alloc] init];
[self.b addObserver:self forKeyPath:@"obj" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:NULL];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.b.obj = @"hello";
}
- (void)dealloc {
[self.b removeObserver:self forKeyPath:@"obj"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@--%@--%@", [self class], object, change);
NSLog(@"%@", [change objectForKey:NSKeyValueChangeNewKey]);
NSLog(@"%@", [change objectForKey:NSKeyValueChangeOldKey]);
}
@end
图标1:是创建B
是继承
父类方法 对B
进行初始化
的时候也给obj
进行赋值
图标2:是点击屏幕是修改obj
是进去的
总结:其实又上面可以看出来KVO其实是对监听属性的set方法进行了某些处理
简单概述下 KVO 的实现:
当你观察一个对象时,一个新的类会动态被创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。自然,重写的 setter 方法会负责在调用原 setter方法之前和之后,通知所有观察对象值的更改。最后把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。
原来,这个中间类,继承自原本的那个类。不仅如此,Apple 还重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类。
使用注意:
[self.b addObserver:`self` forKeyPath:@"obj" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:NULL];
在以上方法中其实添加观察,并不会对self进行
强引用
(即不会对其引用计算器
进行+1),但是为什么我们往往需要在dealloc
析构中,删除对其的监听了,原因为啥???
在dealloc
析构中,删除对其的监听原因
其实原因很简单:和KVO的特性有关,KVO主要是一对多,不像代理的一对一,只要添加了观察后,只要观察的内容进行改变都会来的对应的
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
方法,如果在这个方法中引用了self
,那么如果当前类已经释放调了,因为之前添加过观察,当监听的值改变后,都会来到- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
,当前对象已经被释放了,调野指针,程序直接崩溃,故需要在dealloc
析构中,删除对其的监听
因为B是继承A的本身是没有obj这个属性的 下图可以看出其实这个属性是A的