iOS面试题之KVO

1,921 阅读3分钟

KVO

  • 实现原理

  • 如何手动关闭kvo

  • 通过KVC修改属性会触发KVO么

  • 哪些情况下使用kvo会崩溃,怎么防护崩溃

  • kvo的优缺点

KVO

在开发过程中我们经常使用KVO,下面解答一下KVO相关的问题.

KVO的实现原理

通过runtime派生子类的方式 复写相关需要KVO监听的属性,在该属性setter之前和之后调用NSObject的监听方法,这样KVO就实现了属性变换前后的回调.

KVO派生的子类具体格式应该是:NSKVONotifying_+类名的类 eg: NSKVONotifying_Person

下面示例代码为Person类的name添加KVO的模拟实验

`1 2 3 4 5 6 7 8 9 10 11 12 13

- (void)setName:(NSString *)name{
    _NSSetObjectValueAndNotify();
}

void _NSSetObjectValueAndNotify {
    [self willChangeValueForKey:@"name"];
    [super setName:name];
    [self didChangeValueForKey:@"name"];
}

- (void)didChangeValueForKey:(NSString *)key{
    [observe observeValueForKeyPath:key ofObject:self change:nil context:nil];
}` 

问题来了如何动态创建类呢?

`1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

//动态创建XXCustomClass
Class customClass = objc_allocateClassPair([NSObject class], "XXCustomClass", 0);
// 添加实例变量
class_addIvar(customClass, "age", sizeof(int), 0, "i");
// 动态添加方法
class_addMethod(customClass, @selector(hahahha), (IMP)hahahha, "V@:");

//需要实现的方法
void hahahha(id self, SEL _cmd)
{
    NSLog(@"hahahha====");
}

- (void)hahahha{

}

//最后注册到运行时环境
objc_registerClassPair(customClass);` 

V@:表示方法的参数和返回值

具体原理以及自定义实现KVO可以参考KVO的详解及底层是如何实现的

PS;如果你想了解更多可iOS技术交流,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

如何手动关闭KVO?

被观察的对象复写如下方法 返回NO即可关闭KVO

`1 2 3

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
	return NO;
}` 

如果关闭后还想触发 KVO的话 修改需要手动调用在变量setter的前后 主动调用 willChangeValueForKey:didChangeValueForKey:

通过KVC修改属性会触发KVO么?

会的

哪些情况下使用kvo会崩溃,怎么防护崩溃?

使用不当 会crash,比如:

- 添加和移出不是成对出现且存在多线程添加KVO的情况,经常遇到的crash是移出 - 内存dealloc的时候 或者对象销毁前没有正确移出Observer

如何防护?

1.注意移出对象 匹配
2.内存野指针问题,一定要在对象销毁前移出观察者 3.可以使用第三方库BlockKit添加KVO,blockkit内部会自动移除Observer避免crash.

KVO的优缺点

优点:

- 方便两个对象间同步状态(keypath)更加方便,一般都是在A类要观察B类的属性的变化.
- 非侵入式的得到某内部对象的状态改变并作出响应.(就是在不改变原来对象类的代码情况下即可做出对该对象的状态变化进行监听)
- 可以嵌入更改前后的两个时机的状态. - 可以通过Keypaths对嵌套对象的监听.

缺点:

- 需要手动移除观察者,不移除容易造成crash.
- 注册和移出成对匹配出现.
- keypath参数的类型String, 如果对象的成员变量被重构而变化字符串不会被编译器识别而报错.
- 实现观察的方式是复写NSObjec的相关KVO的方法,应该更加面向protocol的方式会更好.

总结

文章到这里就结束了,如果你有什么意见和建议欢迎给我留言。