KVO

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

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

1、利用RuntimeAPI动态生成一个子类NSKVONotifying_******,并且让instance对象的isa指向这个生成的子类
2、当修改instance对象的属性时,会调用Foundation的_NSSetObjectValueAndNotify函数(内部调用方法)
2.1、[self willChangeValueForKey:@“name”];
2.2、调用父类的set方法 [super setName:name];
2.3、[self didChangeValueForKey:@“name”];
2.3.1、didChangeValueForKey:方法里面做的操作(通知监听器)
[observe observeValueForKeyPath:@“name” ofObject:self change:nil context:nil];
注意:KVO之所以能监听,是因为重写了set方法,所以一般情况下如果没有set方法则不能被监听(手动调动willChangeValueForKey: 和 didChangeValueForKey:方法 – 可以手动触发KVO)

示例代码

@interface TBPerson : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation TBPerson

@end

@interface ViewController ()

@property (nonatomic, strong) TBPerson *person;
@property (nonatomic, strong) TBPerson *person1;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person = [[TBPerson alloc] init];
    self.person1 = [[TBPerson alloc] init];
    
    self.person.name = @"1";
    self.person1.name = @"10";
    
    NSKeyValueObservingOptions option = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
    [self.person addObserver:self forKeyPath:@"name" options:option context:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.person.name = [NSString stringWithFormat:@"%d",arc4random()%10];
    self.person1.name = [NSString stringWithFormat:@"%d",arc4random()%10];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"检查到%@的%@属性发生了变化%@",object,keyPath,change);
}

@end

(lldb) po self.person->isa
NSKVONotifying_TBPerson
(lldb) po self.person1->isa
TBPerson

/*
	self.person->isa 表示 self.person的类对象为 NSKVONotifying_TBPerson
	self.person1->isa 表示 self.person1的类对象为 TBPerson
*/

在这里插入图片描述

未使用KVO监听的对象

在这里插入图片描述

使用了KVO监听的对象

在这里插入图片描述

============================== 以下证明NSKVONotifying_TBPerson重写了setNmae方法 ==============================
/*
	打印监听对象的setName方法的IMP
	可以看到此时的set方法调用的是Foundation框架中的_NSSetObjectValueAndNotify方法
*/
(lldb) p/x [self.person methodForSelector:@selector(setName:)]
(IMP) $1 = 0x00007fff25721c7a (Foundation`_NSSetObjectValueAndNotify)

============================== 以下证明监听内部调用哪些方法以及调用顺序==============================
/*
	_NSSetObjectValueAndNotify函数里面做的操作
	1、[self willChangeValueForKey:@"name"];
	2、调用父类的set方法 [super setName:name];
	3、[self didChangeValueForKey:@"name"];
		didChangeValueForKey:方法里面做的操作(通知监听器)
		[observe observeValueForKeyPath:@"name" ofObject:self change:nil context:nil];
*/

@interface TBPerson : NSObject

@property (nonatomic, strong) NSString *name;

@end

@implementation TBPerson

- (void)setName:(NSString *)name{
    _name = name;
    NSLog(@"setName:");
}

-(void)willChangeValueForKey:(NSString *)key{
    [super willChangeValueForKey:key];
    NSLog(@"willChangeValueForKey:");
}

-(void)didChangeValueForKey:(NSString *)key{
    NSLog(@"didChangeValueForKey: --- begin");
    [super didChangeValueForKey:key];
    NSLog(@"didChangeValueForKey: --- end");
}

@end
//	打印结果如下
KVO[24298:1415295] willChangeValueForKey:
KVO[24298:1415295] setName:
KVO[24298:1415295] didChangeValueForKey: --- begin
KVO[24298:1415295] 检查到<TBPerson: 0x600002315030>的name属性发生了变化{
    kind = 1;
    new = 2;
    old = 1;
}
KVO[24298:1415295] didChangeValueForKey: --- end

============================== 以下证明NSKVONotifying_TBPerson的class对象存放有哪些方法 ==============================
/*
    返回方法列表函数
    Method * class_copyMethodList(Class cls, unsigned int *outCount)
    Class cls 传入类、元类对象
    unsigned int *outCount 传入int的地址 (一个方法有多个返回值)
 */
unsigned int count;
Method *methodList = class_copyMethodList(object_getClass(self.person), &count);

NSMutableArray *methodArr = [NSMutableArray array];
for (int i = 0; i < count; i++) {
    //  获取方法 指针类似于数组
    Method method = methodList[i];
    //  获取方法的名称
    NSString *methodName = NSStringFromSelector(method_getName(method));
    
    [methodArr addObject:methodName];
}
//  C 语言如果是copy、create出来的需要释放
free(methodList);
NSLog(@"%@",methodArr);

// 以下为打印的方法数组
KVO[24425:1425376] (
    "setName:",
    class,
    dealloc,
    "_isKVOA"
)
PS 此文为学习 李明杰 老师的 iOS底层原理课程所写笔记
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值