iOS下KVO使用过程中的陷阱
来源:互联网 发布:评价神户制钢 知乎 编辑:程序博客网 时间:2024/05/11 00:31
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。
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱
- iOS下KVO使用过程中的陷阱(转)
- KVO使用过程中的陷阱
- kvo使用过程中的陷阱
- KVO——使用过程中的陷阱
- iOS-KVO使用避免的陷阱
- iOS中的KVO使用
- iOS中的KVO使用
- ios中的kvo模式使用实例
- iOS中的KVO
- KVO陷阱
- git日常命令
- Struts2,action中用spring注入,启动服务时注入成功,action运行时对象又变成null了
- Python机器学习Numpy, Scipy, Pandas, Scikit-learn, Matplotlib, Keras, NN速查手册
- 糟糕的每日站会
- 图的深度优先遍历以及广度优先遍历
- iOS下KVO使用过程中的陷阱
- PHP获取上周、本周、上月、本月、本季度、上季度时间方法大全
- eclispe中的maven项目使用spring报 org.springframework.web.filter.CharacterEncodingFilter 找不到
- 2017年第0届浙江工业大学之江学院程序设计竞赛决赛 G.qwb去面试【贪心】
- 面试了那么些软件测试人发现的问题
- linux下Telnet问题总结
- Android Studio关联Git及使用
- Android动画详解总览
- Sublime Text 3 快捷键精华版