-
KVO 的全称是 Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变
-
简单应用的示例代码:
-
Person 类:
@interface Person : NSObject @property (nonatomic,assign) int age; @end @implementation Person @end
-
ViewController 类:
#import "ViewController.h" #import "Person.h" @interface ViewController () @property (nonatomic,strong) Person *person1; @property (nonatomic,strong) Person *person2; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.person1 = [[Person alloc] init]; self.person1.age = 10; self.person2 = [[Person alloc] init]; self.person2.age = 20; //给 person 对象添加 KVO监听 [self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { self.person1.age = 20; self.person2.age = 30; } //当监听对象对象的属性值发生改变时,就会调用 - (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
当程序运行后,点击手机屏幕,输出结果如下:
监听到 <Person: 0x6000002f45c0> 的 age 属性值改变了 -- {
kind = 1;
new = 20;
old = 10;
}
-
-
KVO 本质探究
-
为了看出问题所在,我们知道 runtime 中的 object_getClass 返回的是一个对象的 isa 指针,我们将 touchesBegan 方法修改一下,如下所示:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { self.person1.age = 20; self.person2.age = 30; NSLog(@"%p",object_getClass(self.person1)); NSLog(@"%p",object_getClass(self.person2)); }
我们将断点打在最后一行的下面,当程序运行时,点击手机屏幕,程序运行停在断点处,输出结果如下:
0x60000358c060
0x1044fd248
object_getClass( )方法如果传入的是实例对象,则返回的一个类的类对象,并且是惟一的。但是我们会惊讶地发现,上面拿到的却是两个地址值,说明是两个不同的类对象。为了看清楚这两个类对象分别对应什么类型,我们继续通过 lldb 动态调试器分别打印这两个地址值对应的类型,调试过程如下:
(lldb) po 0x60000358c060
NSKVONotifying_Person
(lldb) po 0x1044fd248
Person
此时可以看到,没有添加 KVO 监听的 person2 类型还是 Person,而添加了 KVO 监听的 person1 类型则变成了 NSKVONotifying_Person。
结论:由此可以看出,OC 在程序运行的时候,给添加了KVO 监听的实例对象动态创建了一个类,该类的类名为:NSKVONotifying_xxx。
-
为了进一步验证我们的想法,我们手动在工程中新建一个类,类名为:NSKVONotifying_Person,重新运行此程序,Xcode 会提示:
KVO failed to allocate class pair for name NSKVONotifying_Person, automatic key-value observing will not work for this class
从另一个角度证明,当我们手动创建的类与程序在运行时要动态生成的类的类名冲突时,则系统无法完成KVO动态生成类的工作,此时我们的 KVO 的相关代码也不起作用。
-
网友评论