KVO
Key-Value Observing,键值监听,可以用于监听对象属性值的改变
@interface Person : NSObject
@property(nonatomic,assign)NSInteger age;
@end
#import "Person.h"
@implementation Person
- (void)setAge:(NSInteger)age
{
_age = age;
NSLog(@"%s",__FUNCTION__);
}
- (void)willChangeValueForKey:(NSString *)key
{
[super willChangeValueForKey:key];
NSLog(@"%s",__FUNCTION__);
}
- (void)didChangeValueForKey:(NSString *)key
{
[super didChangeValueForKey:key];
NSLog(@"%s",__FUNCTION__);
}
@end
self.ei = [[Person alloc] init];
self.ei.age = 18;
NSLog(@"%@",object_getClass(self.ei)); // Person
NSKeyValueObservingOptions options = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
[self.ei addObserver:self forKeyPath:@"age" options:options context:@"ei.age"];
NSLog(@"%@",object_getClass(self.ei)); // NSKVONotifying_Person
NSLog(@"%@",[object_getClass(self.ei) superclass]); // Person
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"监听到%@的%@值改变 %@ %@",object,keyPath,change,context);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.ei.age = 20;
}
- (void)dealloc
{
[self.ei removeObserver:self forKeyPath:@"age" context:@"ei.age"];
}
总结
- 添加监听后,runtime动态生成一个NSKVONotifying_Person子类,让实例对象isa指向这个子类
- 在全新的子类里重写被监听的属性setter方法,当执行setter方法时会调用_NSSetXXXValueAndNotify函数
- 函数先调用willChangeValueForKey
- 在调用setter
- 最后调用didChangeValueForKey
- 内部触发observeValueForKeyPath: ofObject: change: context:
应用场景
- 监听UIScrollView的偏移量,改变导航栏背景颜色
- 给TextView增加pleaceHolder,通过KVO监听文本是否输入隐藏展示pleaceHolder
- ......