【原】iOS下KVO使用过程中的陷阱

简介:

KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应。网上广为流传普及的一个例子是利用KVO检测股票价格的变动,例如这里。这个例子作为扫盲入门还是可以的,但是当应用场景比较复杂时,里面的一些细节还是需要改进的,里面有多个地方存在crash的危险。本文旨在逐步递进深入地探讨出一种目前比较健壮稳定的KVO实现方案,弥补网上大部分教程的不足!

首先,假设我们的目标是在一个UITableViewController内对tableview的contentOffset进行实时监测,很容易地使用KVO来实现为。

在初始化方法中加入:

1
[_tableView addObserver: self  forKeyPath:@ "contentOffset"  options: NSKeyValueObservingOptionNew  context: nil ];

在dealloc中移除KVO监听:

1
[_tableView removeObserver: self  forKeyPath:@ "contentOffset"  context: nil ];

添加默认的响应回调方法:

1
2
3
4
5
- ( void )observeValueForKeyPath:( NSString  *)keyPath ofObject:( id )object
                         change:( NSDictionary  *)change context:( void  *)context
{
        [ self  doSomethingWhenContentOffsetChanges];
}

好了,KVO实现就到此完美结束了,拜拜。。。开个玩笑,肯定没这么简单的,这样的代码太粗糙了,当你在controller中添加多个KVO时,所有的回调都是走同上述函数,那就必须对触发回调函数的来源进行判断。判断如下:

1
2
3
4
- ( void )observeValueForKeyPath:( NSString  *)keyPath ofObject:( id )object
                         change:( NSDictionary  *)change context:( void  *)context
{
     if  (object == _tableView && [keyPath isEqualToString:@ "contentOffset" ]) {
1
[ self  doSomethingWhenContentOffsetChanges];
1
} }

你以为这样就结束了吗?答案是否定的!我们假设当前类(在例子中为UITableViewController)还有父类,并且父类也有自己绑定了一些其他KVO呢?我们看到,上述回调函数体中只有一个判断,如果这个if不成立,这次KVO事件的触发就会到此中断了。但事实上,若当前类无法捕捉到这个KVO,那很有可能是在他的superClass,或者super-superClass...中,上述处理砍断了这个链。合理的处理方式应该是这样的:

1
2
3
4
5
6
7
8
9
- ( void )observeValueForKeyPath:( NSString  *)keyPath ofObject:( id )object
                         change:( NSDictionary  *)change context:( void  *)context
{
     if  (object == _tableView && [keyPath isEqualToString:@ "contentOffset" ]) {
         [ self  doSomethingWhenContentOffsetChanges];
else  {
         [ super  observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

 

这样就结束了吗?答案仍旧是否定的。潜在的问题有可能出现在dealloc中对KVO的注销上。KVO的一种缺陷(其实不能称为缺陷,应该称为特性)是,当对同一个keypath进行两次removeObserver时会导致程序crash,这种情况常常出现在父类有一个kvo,父类在dealloc中remove了一次,子类又remove了一次的情况下。不要以为这种情况很少出现!当你封装framework开源给别人用或者多人协作开发时是有可能出现的,而且这种crash很难发现。不知道你发现没,目前的代码中context字段都是nil,那能否利用该字段来标识出到底kvo是superClass注册的,还是self注册的?

回答是可以的。我们可以分别在父类以及本类中定义各自的context字符串,比如在本类中定义context为@"ThisIsMyKVOContextNotSuper";然后在dealloc中remove observer时指定移除的自身添加的observer。这样iOS就能知道移除的是自己的kvo,而不是父类中的kvo,避免二次remove造成crash。

本文转自编程小翁博客园博客,原文链接:http://www.cnblogs.com/wengzilin/p/4346775.html,如需转载请自行联系原作者

相关文章
|
Web App开发 数据安全/隐私保护 Android开发
HBuilder包装iOS APP上App Store的详细过程
HBuilder包装iOS APP上App Store的详细过程
|
存储 安全 IDE
android studio 输出apk过程,apk中的文件格式(bsh文件,dex文件),JVM、DVM、ART的区别,IOS与安卓的区别,ART和Dalvi
android studio 输出apk过程,apk中的文件格式(bsh文件,dex文件),JVM、DVM、ART的区别,IOS与安卓的区别,ART和Dalvi
211 0
android studio 输出apk过程,apk中的文件格式(bsh文件,dex文件),JVM、DVM、ART的区别,IOS与安卓的区别,ART和Dalvi
|
存储 iOS开发
iOS Principle:KVO(下)
iOS Principle:KVO(下)
105 0
iOS Principle:KVO(下)
|
存储 C语言 iOS开发
iOS Principle:KVO(上)
iOS Principle:KVO(上)
119 0
iOS Principle:KVO(上)
|
安全 iOS开发
iOS探索 -- KVO 的原理分析
KVO (key-value-observing) 是一种 键值观察 机制, 它允许当前对象去观察目标对象的某个属性的变化; 当被观察对象的属性发生变化后, 会通过特定方法通知观察者对象属性变化的一些情况内容, 观察者对象拿到变化情况后做出相关操作。
150 0
|
Android开发 iOS开发
iOS开发:KVC与KVO
KVC 就是键值编码(key-value-coding),可以直接访问对象的属性,或者给对象的属性赋值。黑魔法之一,很多高级的iOS开发技巧都是基于KVC实现的。 KVO 是键值观察者(key-value-observing)。实现方式:通过对某个对象的某个属性添加观察者,当该属性改变,就会调用”observeValueForKeyPath:”方法,为我们提供一个“对象值改变了!”的时机进行一些操作。
203 0
iOS开发:KVC与KVO
|
编译器 iOS开发
iOS-底层原理 23:KVO 底层原理
iOS-底层原理 23:KVO 底层原理
136 0
iOS-底层原理 23:KVO 底层原理
|
iOS开发
iOS自动化真机测试验证环境过程中常见问题解析
iOS自动化真机测试验证环境过程中常见问题解析
|
监控 iOS开发
IOS开发-KVO
观察者,观察对象属性的变化,当被观察者该属性发生变化时,观察者会接收到通知,可以在回调函数中做相应的处理
iOS自动化真机测试验证环境过程中常见问题解析
本章节主要讲解 iOS 自动化真机配置以及在 iOS 真机执行自动化时常见问题与解决方法。 ## 真机使用的Capability 与模拟器不同,真机测试需要如下的 Capability 方式一:设置 App 路径,启动 App(自动安装 App) ``` { "app": "/Users/seveniruby/Library/Developer/Xcode/DerivedData/UI