【问题标题】:Crash while de-registration for KVO注销 KVO 时崩溃
【发布时间】:2014-08-11 22:50:55
【问题描述】:

我的应用出现了一些随机崩溃(尽管在执行相同步骤时无法重现)。我正在观察滚动视图的 contentOffset 属性,以便在它发生变化时采取一些措施。

但是我的以下 KVO 注册和注销代码(随机)低于异常。

这里有没有可以应用的安全检查。

*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <MyPagingController 0x1f05e460> for the key path "contentOffset" from <UIScrollView 0x1f0a8fd0> because it is not registered as an observer.'

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

- (void)viewWillDisappear:(BOOL)iAnimated {
    [super viewWillDisappear:iAnimated];
    [self.scrollView removeObserver:self forKeyPath:@"contentOffset"];
}

【问题讨论】:

标签: ios objective-c cocoa-touch key-value-observing


【解决方案1】:

您的退订代码比订阅代码更容易受到攻击。不幸的是,KVO 不能很好地处理这个问题,并且失败会引发异常,而不是像您期望的那样什么都不做。您要么需要确保它只被命中一次,要么至少像这样捕获异常:

@try 
{
  [self.scrollView removeObserver:self forKeyPath:@"contentOffset"];
} 
@catch (NSException * __unused exception) {}
}

【讨论】:

【解决方案2】:

尽管我很喜欢 KVO,但平衡观察和删除的需求是一个真正的问题。如果您将一个被 KVO 观察的对象设为 nil 而没有告诉发送类取消注册您,然后该类尝试向接收者对象发送更新,您也会遇到崩溃。哎呀!访问不正确!

由于没有官方方法可以 ping 以查看您是否添加了观察者(这很荒谬,因为操作系统中发生的大部分事情都是基于 KVO),我只使用一个简单的标志系统来确保没有任何东西被添加超过一次。在下面的示例中,我有一个 bool 标志来跟踪观察者的状态。

@interface RepTableViewController ()
@property (nonatomic, assign) BOOL KVOSet;
@end

#pragma mark - KVOObserving

- (void)_addKVOObserving
   {
      //1. If the BOOL flag shows we are already observing, do nothing.
      if (self.KVOSet) return;

      //2. Set the flag to YES BEFORE setting the observer as there's no guarantee it will happen immediately.
      self.KVOSet = YES;

      //3. Tell your class to add you up for the object / property you want to observe.
      [[ELRepresentativeManager sharedManager]addObserver:self
       forKeyPath:@"myRepresentative"
       options:NSKeyValueObservingOptionNew
       context:nil];
   }

- (void)_removeKVOObserving
   {
       //1. Do nothing if we have not set an observer
       if (!self.KVOSet) return;

       //2. If we have an observer, set the BOOL flag to NO before removing
       self.KVOSet = NO;

       //3. Remove the observer
       [[ELRepresentativeManager sharedManager] removeObserver:self  
       forKeyPath:@"myRepresentative" context:nil];
  }

是的,编码警察不赞成使用标志检查状态。但是除了豆子计数之外,真的没有其他方法可以确定。

无论您做什么,请记住,即使在调用 viewWillDisappear 之后,某些类也需要观察(但视图仍然存在于层次结构中的某个位置),因此您不能只执行从 viewWillAppear/WillDisappear 中观察/删除的技巧。您可能需要对创建视图(并将销毁它)的对象使用委托回调,以确保您永远不会让 KVO 调用类挂起。话虽如此,我不是这方面的专家,所以我敢肯定,有人可以比我的专利豆计数更巧妙地做到这一点,哈哈

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-26
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-16
    相关资源
    最近更新 更多