iOS學習-KVO

kvo

一、是個啥

KVO全稱為Key Value Observing,鍵值監(jiān)聽,可以用于監(jiān)聽某個對象屬性值的改變。
是觀察者設計模式的一種實現(xiàn)。

二、用法

簡單試用

#pragma mark - KVO

-(void)KVOTest{
    
    self.person = [[Person alloc] init];
    self.person.age = 10;

    //給person對象添加監(jiān)聽對象
    [self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"123"];

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.person.age = 20;
}

//響應方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    //觀察者觀察name的變化,當點擊屏幕,改變name的值,chang就會捕獲新值
    
    NSLog(@"%@-%@-%@-%@",keyPath,object,change,context);
}

//移除監(jiān)聽
-(void)dealloc{
    
    [self.person removeObserver:self forKeyPath:@"age"];
}


三、KVO的底層是怎么實現(xiàn)的?

 self.xiaoming1 = [[XiaoMing alloc] init];
    self.xiaoming1.age = 1;
    
    self.xiaoming2 = [[XiaoMing alloc] init];
    self.xiaoming2.age = 2;
    
    [self.xiaoming1 addObserver:self //本控制器來監(jiān)聽
                       forKeyPath:@"age"http://監(jiān)聽xiaoming1 age 的變化
                          options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//通常選這2個,下面會解釋
                          context:@""];//下面會演示
1、疑問

走的都是XiaoMing類方法setAge,為什么xiaoming1的age值,VC能監(jiān)聽到值的改變,xiaoming2不能?
這不是setAge的問題,是對象的問題
我們來打印一下這2個對象的isa,發(fā)現(xiàn)不一樣


image.png
2、本質分析

未使用kvo監(jiān)聽的對象


請忽略圖中的命名

使用kvo監(jiān)聽的對象


請忽略圖中的命名.png

NSKVONotifying_XiaoMing是使用runtime動態(tài)創(chuàng)建的一個類,這就是OC強大的地方,可以在運行的過程中,自己創(chuàng)建一個類。
NSKVONotifying_XiaoMing是XiaoMing的子類。

3、本質驗證

打印一下xiaoming1 、xiaoming2的類對象

 NSLog(@"??監(jiān)聽之前--xiaoming1的類對象:%@-xiaoming2的類對象:%@",object_getClass(self.xiaoming1),
                                                      object_getClass(self.xiaoming2));
    
 [self.xiaoming1 addObserver:self //本控制器來監(jiān)聽
                       forKeyPath:@"age"http://監(jiān)聽xiaoming1 age 的變化
                          options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//通常選這2個,下面會解釋
                          context:@""];//下面會演示

  NSLog(@"??監(jiān)聽之后--xiaoming1的類對象:%@-xiaoming2的類對象:%@",object_getClass(self.xiaoming1),
    object_getClass(self.xiaoming2));
image.png

打印一下xiaoming1 、xiaoming2的setAge方法實現(xiàn)

 NSLog(@"??監(jiān)聽之前--方法實現(xiàn):%p-%p",[self.xiaoming1 methodForSelector:@selector(setAge:)],
          [self.xiaoming2 methodForSelector:@selector(setAge:)]);
    
    [self.xiaoming1 addObserver:self //本控制器來監(jiān)聽
                       forKeyPath:@"age"http://監(jiān)聽xiaoming1 age 的變化
                          options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld//通常選這2個,下面會解釋
                          context:@""];//下面會演示
NSLog(@"??監(jiān)聽之后--方法實現(xiàn):%p-%p",[self.xiaoming1 methodForSelector:@selector(setAge:)],
    [self.xiaoming2 methodForSelector:@selector(setAge:)]);
image.png

再來看一下方法實現(xiàn)


image.png

打印一下類對象和元類對象

NSLog(@"??xiaoming1:類對象-%@,元類對象-%@",object_getClass(self.xiaoming1),object_getClass(object_getClass(self.xiaoming1)));
     NSLog(@"??xiaoming2:類對象-%@,元類對象-%@",object_getClass(self.xiaoming2),object_getClass(object_getClass(self.xiaoming2)));
image.png
4、Foundation里的_NSSetValueAndNotify的實現(xiàn)

如果會點逆向的知識可能會好探索一些,這里直接說結論

打印調用順序

-(void)setAge:(int)age{
    
    _age = age;
    NSLog(@"setAge");
}

-(void)willChangeValueForKey:(NSString *)key{
    
    [super willChangeValueForKey:key];//為了干擾以前的實現(xiàn),調用super
    NSLog(@"willChangeValueForKey");
}

-(void)didChangeValueForKey:(NSString *)key{
    
    NSLog(@"didChangeValueForKey-開始");
    [super didChangeValueForKey:key];
    NSLog(@"didChangeValueForKey-結束");
}
image.png

四、問題

1、iOS用什么方式實現(xiàn)對一個對象的KVO?(KVO的本質是什么?)

1、使用runtime API動態(tài)生產(chǎn)一個子類,并且讓實例對象的isa指向這個全新的
子類
2、當修改對象的屬性時,會調用Foundation里的_NSSetXXXXAndNotify的函數(shù)

  • willChangeValueForKey
  • 父類原來的setter
  • didChangeValueForKey
  • 內部會觸發(fā)監(jiān)聽器observe的監(jiān)聽方法:observeValueForKeyPath

2、如何手動觸發(fā)KVO?

這個題的理解應該是不改變對象的屬性值,也能觸發(fā)KVO

手動調用willChangeValueForKey和didChangeValueForKey方法。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    [self.xiaoming1 willChangeValueForKey:@"age"];
    [self.xiaoming1 didChangeValueForKey:@"age"];
}
image.png

3、直接修改成員變量會不會觸發(fā)KVO?
不會

4、KVO的運用場景?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容