KVO的定義
KVO 的全稱是
Key-Value Observing
兄春,俗稱“鍵值監(jiān)聽(tīng)”衫樊,可以用于監(jiān)聽(tīng)某個(gè)對(duì)象屬性值的改變.
使用
-(void)viewDidLoad
{
[super viewDidLoad];
self.people1 = [[HPPeople alloc] init];
self.people2 = [[HPPeople alloc] init];
NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.people1 addObserver:self forKeyPath:@"age" options:option context:nil];
}
-(void)dealloc
{
[self.people1 removeObserver:self forKeyPath:@"age"];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//people1的類對(duì)象為HPPeople
self.people1.age = 5;
//people2的類對(duì)象為NSKVONotifying_HPPeople
self.people2.age = 3;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"監(jiān)聽(tīng)到%@的屬性值%@發(fā)生了變化 %@ - %@",object,keyPath,change,context);
}
點(diǎn)擊打印結(jié)果
監(jiān)聽(tīng)到<HPPeople: 0x6000013992e0>的屬性值age發(fā)生了變化 {
kind = 1;
new = 5;
old = 0;
} - (null)
添加斷點(diǎn)
people1
對(duì)象和people2
對(duì)象的賦值操作汽煮,其他屬性都一樣,people1
對(duì)象的age
變化了會(huì)打印,而people2不會(huì)打印.然后我們打斷點(diǎn)發(fā)現(xiàn)people1
的類對(duì)象發(fā)生了變化,變成NSKVONotifying_HPPeople
我們嘗試著打印people1對(duì)象和people2對(duì)象添加觀察者前后的setAge:
方法的實(shí)現(xiàn)
修改代碼
-(void)viewDidLoad
{
[super viewDidLoad];
self.people1 = [[HPPeople alloc] init];
self.people2 = [[HPPeople alloc] init];
NSLog(@"people1未添加觀察者之前 %p %p",[self.people1 methodForSelector:@selector(setAge:)],[self.people2 methodForSelector:@selector(setAge:)]);
NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.people1 addObserver:self forKeyPath:@"age" options:option context:nil];
NSLog(@"people1添加觀察者之后 %p %p",[self.people1 methodForSelector:@selector(setAge:)],[self.people2 methodForSelector:@selector(setAge:)]);
}
打印結(jié)果
2020-10-16 20:22:17.118174+0800 gcd[67348:5498682] people1未添加觀察者之前 0x1040c1d30 0x1040c1d30
2020-10-16 20:22:17.118736+0800 gcd[67348:5498682] people1添加觀察者之后 0x1044478bc 0x1040c1d30
(lldb) p (IMP)0x1040c1d30
(IMP) $0 = 0x00000001040c1d30 (gcd`-[HPPeople setAge:] at HPPeople.h:15)
(lldb) p (IMP)0x1044478bc
(IMP) $1 = 0x00000001044478bc (Foundation`_NSSetLongLongValueAndNotify)
(lldb)
那么添加了觀察者對(duì)象people1的setAge:
方法的實(shí)現(xiàn)變成Foundation
框架的_NSSetLongLongValueAndNotify
函數(shù)
我們通過(guò)反編譯工具Hopper拿到Foundation
框架的偽代碼從中去查找_NSSetLongLongValueAndNotify
函數(shù)看到的是匯編代碼献汗,大致的實(shí)現(xiàn)過(guò)程如下偽代碼
-(void)_NSSetLongLongValueAndNotify //偽代碼
{
[self willChangeValueForKey:@"age"];
//中間是原來(lái)setter的實(shí)現(xiàn)
[self didChangeValueForKey:@"age"];
}
- 調(diào)用willChangeValueForKey:
- 調(diào)用原來(lái)的setter實(shí)現(xiàn)
- 調(diào)用didChangeValueForKey:didChangeValueForKey:內(nèi)部會(huì)調(diào)用observer的observeValueForKeyPath:ofObject:change:context:方法
KVO的本質(zhì)用圖來(lái)展示如下
結(jié)論:
KVO的本質(zhì)是運(yùn)用RuntimeApi動(dòng)態(tài)的生成了一個(gè)子類,并讓這個(gè)instance對(duì)象的isa指針指向這個(gè)全新的類王污,當(dāng)修改這個(gè)instance對(duì)象的屬性時(shí)就會(huì)調(diào)用全新子類的setter方法實(shí)現(xiàn)罢吃,這個(gè)setter方法的實(shí)現(xiàn)是調(diào)用了Fundation框架_NSSetXXXValueAndNotify
的方法