OC底层原理:四、KVO

50 阅读1分钟

一、 KVO定义

KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变。

image.png

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ZGPerson : NSObject

@property (nonatomic, assign) int  age;

@end

NS_ASSUME_NONNULL_END

#import "ZGPerson.h"

@implementation ZGPerson

@end

#import "ViewController.h"

#import "ZGPerson.h"

@interface ViewController ()

@property (nonatomic, strong) ZGPerson *person1;
@property (nonatomic, strong) ZGPerson *person2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person1 = [ZGPerson new];
    self.person1.age = 1;
    
    self.person2 = [ZGPerson new];
    self.person2.age = 2;
    
    //给person1对象添加KVO
    [self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
}

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

//当监听对象的属性值发生改变时,就会调用
- (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"];
}

@end

image.png

image.png

二、未使用KVO监听的对象

image.png

三、使用了KVO监听的对象

image.png

四、本质分析、验证

4.1 本质分析

伪代码:

image.png

4.2 本质验证

image.png

image.png

我们看到person1的调用内存地址发生改变,而person2的调用内存地址并没有发生变化。

验证调用setAge:方法时,执行了Foundation _NSSetIntValueAndNotify方法

image.png

NSKVONotifying_ZGPerson的class对象 有自己的 NSKVONotifying_ZGPerson的meta-class对象NSKVONotifying_ZGPerson的class对象isa指向NSKVONotifying_ZGPerson的meta-class对象

image.png

五、_NSSet*ValueAndNotify的内部实现

5.1 查看_NSSet*AndNotify的存在

image.png

5.2 内部实现

image.png

image.png

调用流程:

  • 调用willChangeValueForKey:

  • 调用原来的setter实现

  • 调用didChangeValueForKey:

    • didChangeValueForKey:内部会调用observerobserveValueForKeyPath:ofObject:change:context:方法

六、子类的内部方法

image.png