【原】iOS下KVO使用过程中的陷阱
KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应。网上广为流传普及的一个例子是利用KVO检测股票价格的变动,例如这里。这个例子作为扫盲入门还是可以的,但是当应用场景比较复杂时,里面的一些细节还是需要改进的,里面有多个地方存在crash的危险。本文旨在逐步递进深入地探讨出一种目前比较健壮稳定的KVO实现方案,弥补网上大部分教程的不足!
首先,假设我们的目标是在一个UITableViewController内对tableview的contentOffset进行实时监测,很容易地使用KVO来实现为。
在初始化方法中加入:
- [_tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
在dealloc中移除KVO监听:
- [_tableView removeObserver:self forKeyPath:@"contentOffset" context:nil];
添加默认的响应回调方法:
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
- change:(NSDictionary *)change context:(void *)context
- {
- [self doSomethingWhenContentOffsetChanges];
- }
好了,KVO实现就到此完美结束了,拜拜。。。开个玩笑,肯定没这么简单的,这样的代码太粗糙了,当你在controller中添加多个KVO时,所有的回调都是走同上述函数,那就必须对触发回调函数的来源进行判断。判断如下:
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
- change:(NSDictionary *)change context:(void *)context
- {
- if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
- [self doSomethingWhenContentOffsetChanges];
- } }
你以为这样就结束了吗?答案是否定的!我们假设当前类(在例子中为UITableViewController)还有父类,并且父类也有自己绑定了一些其他KVO呢?我们看到,上述回调函数体中只有一个判断,如果这个if不成立,这次KVO事件的触发就会到此中断了。但事实上,若当前类无法捕捉到这个KVO,那很有可能是在他的superClass,或者super-superClass...中,上述处理砍断了这个链。合理的处理方式应该是这样的:
- - (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。
=======================================================
原创文章,转载请注明 编程小翁@博客园,邮件zilin_weng@163.com,欢迎各位与我在C/C++/Objective-C/机器视觉等领域展开交流!
=======================================================
【原】iOS下KVO使用过程中的陷阱的更多相关文章
- iOS下KVO使用过程中的陷阱 (转发)
iOS下KVO使用过程中的陷阱 KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应.网上广为流传普及的一个例子是利用KV ...
- Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果
目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果 ...
- 在Linux下安装PHP过程中,编译时出现错误的解决办法
在Linux下安装PHP过程中,编译时出现configure: error: libjpeg.(a|so) not found 错误的解决办法 configure: error: libjpeg.(a ...
- windows下创建子进程过程中代码重复执行问题
windows在启动子进程的时候会将主进程文件倒入到子进程中.导入模块就相当于执行这个模块中的代码, 所以第一个print会在主进程中执行一次,又在被导入的过程中在子进程中又执行了一次. p.star ...
- WINDOWS系统下MYSQL安装过程中的注意事项
1.首先MySQL的安装方式有两种:一种是MSI安装方式,很简单就像安装Windows软件一样.另外一种就是ZIP安装方式.这种相对而言比较麻烦.新手推荐MSI安装方式. 安装方式有以下两种: MSI ...
- Java 理论和实践: 了解泛型 识别和避免学习使用泛型过程中的陷阱
Brian Goetz (brian@quiotix.com), 首席顾问, Quiotix 简介: JDK 5.0 中增加的泛型类型,是 Java 语言中类型安全的一次重要改进.但是,对于初次使用泛 ...
- wpf下datagrid使用过程中需要注意的几点(一)
MainWindow.xaml中的代码如下: <DataGrid CanUserAddRows="False" ItemsSource="{Binding}&quo ...
- [原]编译Android源码过程中遇到的问题
编译Android源码的过程参考Android官网介绍: 1.下载Android源码的步骤:https://source.android.com/source/downloading.html 2.编 ...
- win10下安装Wampservice过程中遇到的问题及解决办法
今天在电脑上装Wampserver的时候遇到了几个问题,启动Wampserver无法成功,一直显示橙色.若启动成功Wampserver的图标会显示绿色. 下面的是解决方法 安装 在浏览器中搜索Wamp ...
随机推荐
- Unity3D Android手机开发环境配置,可真机发布调试
此方法配置好,在可以在unity直接发布到手机上,并可以实时调试. 1.配置eclipse环境:首先在官网下载安装包:http://developer.android.com/sdk/index.ht ...
- cp: omitting directory”错误
在linux下拷贝的时候有时候会出现cp:omitting directory的错误 , 是因为目录下面还有子目录,不能直接拷贝 解决办法: 命令:cp -r
- coding代码仓库的配置和代码上传
1.生成ssh 在桌面右键,选择Git Bash Here,进入操作界面,输入“ssh-keygen -t rsa -b 4096 -c "xxxxxxxx@xxx.com(自己注册时绑定的 ...
- 20款优秀的国外 Mobile App 界面设计案例
在下面给大家分享的移动应用程序界面设计作品中,你可以看到不同创意类型的视觉效果.如果你想获得灵感,那很有必要看看下面20个优秀用户体验的移动应用 UI 设计.想要获取更多的灵感,可以访问移动开发分类, ...
- MySQL中的备份和恢复
MySQL备份 MySQL中的逻辑备份是将数据库中的数据备份为一个文本文件,备份的文件可以被查看和编辑.在MySQL中,使用mysaldump工具来完成备份.有以下3种来调用mysqldump: 备份 ...
- EventBus初理解
缘由: 平时工作,因为懒于动笔的原因,也没注重技术和经验的积累,导致之前曾经研究过的问题现在又忘记了,所以要慢慢注重积累,那么就从写作开始,谈谈对工作中碰到的问题进行整理和归纳. 我们 ...
- Java中基本数据类型的存储方式和相关内存的处理方式(java程序员必读经典)
1.java是如何管理内存的 java的内存管理就是对象的分配和释放问题.(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对 ...
- 常见的几种 CSS 水平垂直居中解决办法
用CSS实现元素的水平居中,比较简单,可以设置text-align center,或者设置 margin-left:auto; margin-right:auto 之类的即可. 主要麻烦的地方还是在垂 ...
- P6 EPPM R16.1安装与配置指南(一)
标题 http://www.cnblogs.com/endv/p/5634620.html 安装与配置指南安装与配置指南(数据库)说明哪些How to set up the P6专业数据库和服务器.a ...
- ASP.NET MVC使用动态产生meta
在ASP.NET中,我们是很容易动态为header节点添加meta信息.<动态修改网页Header属性,Title,Meta标签等>http://www.cnblogs.com/insus ...