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
未使用KVO监听的对象
使用了KVO监听的对象
============================== 以下证明NSKVONotifying_TBPerson重写了setNmae方法 ==============================
(lldb) p/x [self.person methodForSelector:@selector(setName:)]
(IMP) $1 = 0x00007fff25721c7a (Foundation`_NSSetObjectValueAndNotify)
============================== 以下证明监听内部调用哪些方法以及调用顺序==============================
@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对象存放有哪些方法 ==============================
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];
}
free(methodList);
NSLog(@"%@",methodArr);
KVO[24425:1425376] (
"setName:",
class,
dealloc,
"_isKVOA"
)
PS 此文为学习 李明杰 老师的 iOS底层原理课程所写笔记