KVO学习

作者: 朝夕向背 | 来源:发表于2018-11-08 10:17 被阅读0次

KVO,俗称键值监听,可以用于监听某个对象属性值的改变。

  • 先简单的演示下KVO的使用方式
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

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

NS_ASSUME_NONNULL_END

#import "Person.h"

@implementation Person
- (void)setAge:(int)age{
    _age = age;
}
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person *person1;
@property (nonatomic, strong) Person *person2;
@end

@implementation ViewController

- (void)dealloc{
    [self.person1 removeObserver:self forKeyPath:@"age"];
    [self.person2 removeObserver:self forKeyPath:@"age"];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person1 = [[Person alloc] init];
    self.person1.age = 20;

    self.person2 = [[Person alloc] init];
    self.person2.age = 19;

    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"height" options:options context:nil];
    NSLog(@"person1添加KVO监听之后-%p",[self.person1 methodForSelector:@selector(setAge:)]);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.person1.age = 22;
    self.person2.age = 23;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"%@ %@ %@",keyPath,object,change);
}
@end

点击手机屏幕,打击结果如下

2018-11-07 15:08:56.214085+0800 KVO[5647:212323] age <Person: 0x6000016739c0> {
    kind = 1;
    new = 22;
    old = 20;
}

在touchesBegan方法内,相当于调用Person类的setAge方法,但是person1调用setAge方法后,又调用observeValueForKeyPath方法、而person2只是调用了setAge方法。由此可见,KVO的调用本质与setAge无关,而是与person1这个实例对象有关。我们在self.person12.age = 23;处打断点,打印后发现person1继承自NSKVONotifying_Person。而person2继承自Person。打印如下:

(lldb) p self.person1.isa
(Class) $0 = NSKVONotifying_Person
  Fix-it applied, fixed expression was: 
    self.person1->isa
person2继承关系图
person1继承关系图
  • 1、当观察对象A时,KVO机制利用runtime API动态的创建NSKVONotifying_A子类,并且让instance对象的isa指针指向这个全新的子类。
  • 2、KVO为NSKVONotifying_A重写观察属性的setter方法,当修改instance的属性时,会调用Foundation的_NSSetXXXValueAndNotify方法。

底层调用情况

self.person1.age = 22处打断点,点击屏幕之后,调出lldb,对person1添加KVO监听之后打印的地址值输出p IMP(0x105e51cf2),打印出结果

p IMP(0x105e51cf2)
(IMP) $0 = 0x0000000105e51cf2 (Foundation _NSSetIntValueAndNotify)

由此说明调用被监听的类中的setAge方法,这个方法内部调用C语言函数_NSSetIntValueAndNotify

下面通过伪代码来说明子类NSKVONotifying_A内部的调用(不能运行)。

#import "NSKVONotifying_Person.h"

@implementation NSKVONotifying_Person
- (void)setAge:(int)age{
    _NSSetIntValueAndNotify();
}
void _NSSetIntValueAndNotify(){
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key{
    [self observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
@end

NSKVONotifying_Person内部重写setAge方法。当age属性值改变时:

  • 1、调用NSKVONotifying_Person类中的setAge方法,这个方法内部调用C语言函数_NSSetIntValueAndNotify
  • 2、这个函数内部首先调用函数willChangeValueForKey方法,然后调用父类的setAge方法,修改age属性值。然后再调用didChangeValueForKey方法。
  • 3、在didChangeValueForKey方法内,调用函数observeValueForKeyPath: ofObject: change: context:监听到属性值的改变。

相关文章

  • KVO学习

    KVO机制学习 什么是KVO?   KVO是Key-Value-Observing的缩写,通过KVO机制对象可以得...

  • OC的KVO学习记录(2)

    仿写KVO的实现KVO原理参考可前一篇OC的KVO学习记录代码github地址:Sameny仿写KVO 注:关键代...

  • KVO学习笔记

    1.KVO初探学习2.KVO 底层原理探索 1.KVO初探学习 移除观察者的重要性 (IOS11之后说不移除是不对...

  • KVO&KVC分析

    KVO和KVC应用还是比较广泛的。所以,今天我们就重新再学习下它们,以加深记忆。 一、KVO 1、KVO(key-...

  • KVO学习

    KVO,俗称键值监听,可以用于监听某个对象属性值的改变。 先简单的演示下KVO的使用方式 点击手机屏幕,打击结果如...

  • KVO学习

    部分代码均可参考AFNetworking文件 1.KVO监听属性(自动监听) 1.1.注册监听 1.2.监听回调 ...

  • KVO 学习

    KVO key-Value Observing 键值监听 ,可以用于监听某个对象属性值的改变 使用了KVO监听的对...

  • KVC、KVO的本质

    这篇文章介绍KVC、KVO的本质。如果你对KVC、KVO不了解,推荐先查看其用法:KVC和KVO学习笔记[http...

  • KVO初探

    最好的学习文档是 KVO苹果官方文档 KVO三步曲 添加观察者 observer:注册KVO通知的对象。观察者必须...

  • iOS 底层探索之KVO

    KVO是Objective-C中的键值监听策略,本篇能学习到以下知识点1、KVO的使用方法大全2、KVO的底层如何...

网友评论

      本文标题:KVO学习

      本文链接:https://www.haomeiwen.com/subject/vzrpxqtx.html