iOS学习-KVO

2020-06-06  本文已影响0人  快乐的tomato
kvo

一、是个啥

KVO全称为Key Value Observing,键值监听,可以用于监听某个对象属性值的改变。
是观察者设计模式的一种实现。

二、用法

简单试用

#pragma mark - KVO

-(void)KVOTest{
    
    self.person = [[Person alloc] init];
    self.person.age = 10;

    //给person对象添加监听对象
    [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"123"];

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.person.age = 20;
}

//响应方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    //观察者观察name的变化,当点击屏幕,改变name的值,chang就会捕获新值
    
    NSLog(@"%@-%@-%@-%@",keyPath,object,change,context);
}

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


三、KVO的底层是怎么实现的?

 self.xiaoming1 = [[XiaoMing alloc] init];
    self.xiaoming1.age = 1;
    
    self.xiaoming2 = [[XiaoMing alloc] init];
    self.xiaoming2.age = 2;
    
    [self.xiaoming1 addObserver:self //本控制器来监听
                       forKeyPath:@"age"//监听xiaoming1 age 的变化
                          options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//通常选这2个,下面会解释
                          context:@""];//下面会演示
1、疑问

走的都是XiaoMing类方法setAge,为什么xiaoming1的age值,VC能监听到值的改变,xiaoming2不能?
这不是setAge的问题,是对象的问题
我们来打印一下这2个对象的isa,发现不一样


image.png
2、本质分析

未使用kvo监听的对象


请忽略图中的命名

使用kvo监听的对象


请忽略图中的命名.png

NSKVONotifying_XiaoMing是使用runtime动态创建的一个类,这就是OC强大的地方,可以在运行的过程中,自己创建一个类。
NSKVONotifying_XiaoMing是XiaoMing的子类。

3、本质验证

打印一下xiaoming1 、xiaoming2的类对象

 NSLog(@"🍎监听之前--xiaoming1的类对象:%@-xiaoming2的类对象:%@",object_getClass(self.xiaoming1),
                                                      object_getClass(self.xiaoming2));
    
 [self.xiaoming1 addObserver:self //本控制器来监听
                       forKeyPath:@"age"//监听xiaoming1 age 的变化
                          options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//通常选这2个,下面会解释
                          context:@""];//下面会演示

  NSLog(@"🍎监听之后--xiaoming1的类对象:%@-xiaoming2的类对象:%@",object_getClass(self.xiaoming1),
    object_getClass(self.xiaoming2));
image.png

打印一下xiaoming1 、xiaoming2的setAge方法实现

 NSLog(@"🍎监听之前--方法实现:%p-%p",[self.xiaoming1 methodForSelector:@selector(setAge:)],
          [self.xiaoming2 methodForSelector:@selector(setAge:)]);
    
    [self.xiaoming1 addObserver:self //本控制器来监听
                       forKeyPath:@"age"//监听xiaoming1 age 的变化
                          options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//通常选这2个,下面会解释
                          context:@""];//下面会演示
NSLog(@"🍎监听之后--方法实现:%p-%p",[self.xiaoming1 methodForSelector:@selector(setAge:)],
    [self.xiaoming2 methodForSelector:@selector(setAge:)]);
image.png

再来看一下方法实现


image.png

打印一下类对象和元类对象

NSLog(@"🍎xiaoming1:类对象-%@,元类对象-%@",object_getClass(self.xiaoming1),object_getClass(object_getClass(self.xiaoming1)));
     NSLog(@"🍎xiaoming2:类对象-%@,元类对象-%@",object_getClass(self.xiaoming2),object_getClass(object_getClass(self.xiaoming2)));
image.png
4、Foundation里的_NSSetValueAndNotify的实现

如果会点逆向的知识可能会好探索一些,这里直接说结论

打印调用顺序

-(void)setAge:(int)age{
    
    _age = age;
    NSLog(@"setAge");
}

-(void)willChangeValueForKey:(NSString *)key{
    
    [super willChangeValueForKey:key];//为了干扰以前的实现,调用super
    NSLog(@"willChangeValueForKey");
}

-(void)didChangeValueForKey:(NSString *)key{
    
    NSLog(@"didChangeValueForKey-开始");
    [super didChangeValueForKey:key];
    NSLog(@"didChangeValueForKey-结束");
}
image.png

四、问题

1、iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

1、使用runtime API动态生产一个子类,并且让实例对象的isa指向这个全新的
子类
2、当修改对象的属性时,会调用Foundation里的_NSSetXXXXAndNotify的函数

  • willChangeValueForKey
  • 父类原来的setter
  • didChangeValueForKey
  • 内部会触发监听器observe的监听方法:observeValueForKeyPath

2、如何手动触发KVO?

这个题的理解应该是不改变对象的属性值,也能触发KVO

手动调用willChangeValueForKey和didChangeValueForKey方法。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    [self.xiaoming1 willChangeValueForKey:@"age"];
    [self.xiaoming1 didChangeValueForKey:@"age"];
}
image.png

3、直接修改成员变量会不会触发KVO?
不会

4、KVO的运用场景?

上一篇 下一篇

猜你喜欢

热点阅读