最近准备面试,总结了一些KVO的问题,答案基本都可以从下边几篇博客找到。
1.什么是KVO?
Key-Value Observer,允许对象监听其他对象的属性的改变,并且在改变时触发相应的事件。一般继承自NSObject的对象都默认支持KVO。
2.基本使用:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// Do any additional setup after loading the view.
//keypath:对应的属性。
//NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld 会把旧值和新值都返回给你,
NSKeyValueObservingOptionInitial会在注册完立马调用一次。
context:可以在接收回调时接收到任意类型的值。
self.per = [Person new];
[self.per addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"zmodo"];
}
- (void)viewDidAppear:(BOOL)animated{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self setValue:@"kvo" forKeyPath:@"per.name"];
});
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.navigationController popViewControllerAnimated:YES];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"keypath:%@-object:%@-change:%@-context:%@",keyPath,object,change,context);
keypath:name-object:<Person: 0x6000029fc570>-change:{
kind = 1;
new = kvo;
old = "<null>";
}-context:zmodo
}
<1>添加观察者,并不会强引用
<2>移除观察者,一般添加和移除要成对出现,要不然容易crash。
3.什么时候触发KVO?
调用属性的setter方法,使用kvc设置属性值时都会触发
4.如何手动触发?
调用[self willChangeValueForKey:@"balance"]和didChangeValueForKey都会触发KVO。
5.如何禁用KVO?
在+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey方法中单独设置就可以了。
6.设置私有变量的值会触发kvo吗? 会
7.KVO的实现原理是怎么样的?
KVO的实现主要依赖于runtime。对于被观察的类,系统会动态的给他生成一个名字为Knofiy_Class的中间类,它是原来类的子类,并且让对象的isa指针指向它,然后重写这个类里对应属性的setter方法。值修改前会调用willChangeValueForKey方法,修改后会调用didChangeValueForKey。
8.KVO的缺点有哪些?
属性名修改后keypath忘记修改很容易导致这样的NSUnknownKeyException 的crash。
语法比较分散。
9.碰到的问题?
<1>.父类中也观察了某个属性,子类中也有,子类把父类的覆盖掉。
在子类中调用父类的方法,并且在父类和子类中都加入相关的判断,需要注意:父类的属性改变时,收到通知时,收到的object也是子类对象。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
if (object == self && [keyPath isEqualToString:@"person"]) {
NSLog(@"CHildkeypath:%@-object:%@-change:%@-context:%@",keyPath,object,change,context);
}else{
// NSLog(@"keypath:%@-object:%@-change:%@-context:%@",keyPath,object,change,context);
}
}
<2>多次removeobserver会crash 如何解决。 www.jianshu.com/p/e68b48f0c…
<3>.多次add会多次收到通知吗? 会
<4>.属性改变了,keypath忘记修改,如何解决。
9.推荐一个Facebook开源的框架KVOController。
如果面试时能把实现原理讲一下就更好了。
参考链接: www.jianshu.com/p/badf5cac0… tech.glowing.com/cn/implemen… www.jianshu.com/p/badf5cac0…