KVO的几个坑

# KVO的几个坑

    昨天同事看一个很普通的bug,viewControllerA和B存在同样一个播放记录列表,在viewControllerA push到B后 对B中的列表进行增删改操作后pop返回到A,此时没有进行主动刷表操作,导致A表仍然调用之前内存中的数据源。
    解决这个问题只需要在表A中对数据源添加一个观察者,数据发生变化时回调自动返回变化的相应内容并主动刷表。
    本身该这个bug很简单,改完这个bug后顺便找了一下KVO相应的坑!
    首先,我们的目标是对nsmutableDictionary进行实时监测,很容易地使用KVO来实现。
    在初始化方法中加入:
    [_playRecordDic addObserver:self forKeyPath:@"palyRecord" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    在dealloc中移除KVO监听:
    [_playRecordDic removeObserver:self forKeyPath:@"palyRecord" context:nil];
    添加默认的响应回调方法:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                    change:(NSDictionary *)change context:(void *)context
    {
       [self doSomethingWhenContentOffsetChanges];
    }
    好了,KVO实现就到此完美结束了,拜拜。。。开个玩笑,肯定没这么简单的,这样的代码太粗糙了,当你在controller中添加多个KVO时,所有的回调都是走同上述函数,那就必须对触发回调函数的来源进行判断。
    我们假设当前类还有父类,并且父类也有自己绑定了一些其他KVO呢?如果这个if不成立,这次KVO事件的触发就会到此中断了。但事实上,若当前类无法捕捉到这个KVO,那很有可能是在他的superClass,或者super-superClass...中,上述处理砍断了这个链。合理的处理方式应该是这样的
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if ([object isEqual:_playRecordDic] && [keyPath isEqualToString:@"palyRecord"])
{
    NSLog(@"改变了");
}
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。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值