1、kvo的简单使用
创建文件MJPerson类,添加属性age、height。
#import "ViewController.h"
#import "MJPerson.h"
@interface ViewController ()
@property(nonatomic,strong)MJPerson *person1;
@property(nonatomic,strong)MJPerson *person2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[MJPerson alloc] init];
self.person1.age =10;
self.person1.height=100;
self.person2 = [[MJPerson alloc] init];
self.person2.age = 9;
self.person2.height = 90;
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld;
//我们给person1加上kvo监听
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"haha"];
[self.person1 addObserver:self forKeyPath:@"height" options:options context:nil];
//我们不给person2加上kvo监听
// [self.person2 addObserver:self forKeyPath:@"age" options:options context:nil];
// [self.person2 addObserver:self forKeyPath:@"height" options:options context:nil];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//person1和person2的age属性都发生变化
self.person1.age = 20;
self.person2.age = 99;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"监听到%@的属性%@发生了变化----%@",object,keyPath,change);
}
-(void)dealloc{
//记得在适当的时候去除监听
[self.person1 removeObserver:self forKeyPath:@"age"];
[self.person1 removeObserver:self forKeyPath:@"height"];
// [self.person2 removeObserver:self forKeyPath:@"age"];
// [self.person2 removeObserver:self forKeyPath:@"height"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
=====================================================
打印结果为:
监听到<MJPerson: 0x17400d110>的属性age发生了变化----{
kind = 1;
new = 20;
old = 10;
}
因为我们去掉了对person2的监听,所以不会有显示person2属性的改变,但是person1的属性age改变被监听了下来。字典change的内容表示改变内容。
2、探究KVO本质
先上example:
创建文件MJPerson类,添加属性age。
#import "ViewController.h"
#import "MJPerson.h"
@interface ViewController ()
@property(nonatomic,strong)MJPerson *person1;
@property(nonatomic,strong)MJPerson *person2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[MJPerson alloc] init];
self.person1.age =10;
self.person2 = [[MJPerson alloc] init];
self.person2.age = 9;
//给person1加上kvo监听
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:nil];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 因为使用了KVO监听self.person1,
// 所以,runtime动态创建一个新的类NSKVONotifying_MJPerson,这个类应该是MJPerson的子类
// self.person1.isa = NSKVONotifying_MJPerson
[self.person1 setAge:20];
// self.person2.isa = MJPerson
[self.person2 setAge:99];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"监听到%@的属性%@发生了变化----%@",object,keyPath,change);
}
-(void)dealloc{
[self.person1 removeObserver:self forKeyPath:@"age"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
原理图如下:
31538461242_.pic.jpg 41538461242_.pic.jpg
伪代码如下:
@implementation NSKVONotifying_MJPerson
-(void)setAge:(int)age{
_NSSetIntValueAndNotify();
}
//伪代码
void _NSSetIntValueAndNotify(){
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
-(void)didChangeValueForKey:(NSString *)key{
[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
@end
3、总结:
<1>KVO的本质是什么?
在程序运行中,利用runtime动态生成一个子类NSKVONotifying_MJPerson(该类是MJPerson的子类)且让对象的isa指向这个全新的子类。通过重写该子类set方法,使得修改对象的属性时,会调用foundation的_NSSetXXXValueAndNotify函数,
- willChangeValueForKey
- 父类原来的set方法
-
didChangeValueForKey
继而触发监听器
<2>如何手动触发KVO?
本来是应该通过改变对象属性值来触发KVO的。如果非要手动触发,可以调用willChangeValueForKey和didChangeValueForKey方法,这样属性值没有发生变化,还能触发KVO监听。
<3>.直接修改成员变量会触发KVO吗?
只有通过触发set方法才会触发KVO,如果使用self.person1->_age这种方法来直接修改对象属性的话,是不会触发对象的set方法的,所以不会触发KVO监听。
网友评论