2016-01-22 68 views
0

我在这里看一个表格视图单元格,我发现这个代码:的keyValue观察者管理对象

- (void)awakeFromNib { 
    [super awakeFromNib]; 
    [self addObserver:self forKeyPath:@"model.isDownloading" options:NSKeyValueObservingOptionNew context:NULL]; 
    [self addObserver:self forKeyPath:@"model.isCached" options:NSKeyValueObservingOptionNew context:NULL]; 
    [self addObserver:self forKeyPath:@"model.isOutDated" options:NSKeyValueObservingOptionNew context:NULL]; 
    [self addObserver:self forKeyPath:@"model.cacheUpdateDate" options:NSKeyValueObservingOptionNew context:NULL]; 
    [self addObserver:self forKeyPath:@"model" options:NSKeyValueObservingOptionNew context:NULL]; 
} 

观察员在dealloc方法去除。 model是一个weak属性,用于接收管理对象(核心数据)。

我收到虚假的崩溃,告诉我,托管对象被删除,但仍有注册观察员。

为什么发生错误对我来说很清楚:该对象在后台某处被移除,但仍然链接到tableview的单元格中。由于单元格上的dealloc在应用程序的生命周期中基本上不会被调用,所以观察者永远不会被删除。由于对核心数据对象的引用是weak,因此它将在后台静默释放 - 至少尝试。这失败了,因为模型仍然被观察到。

我有一些问题:

  • 如果发现像“model.isDownloading”的路径,那么观察者被注册在model对象,而不是在self二传手,是正确的?
  • 是objC足够聪明,如果model被重新分配来处理观察者变化(self.model = newThing要求,即removeObserver被分配newThing之前呼吁model,和观察员需要在那之后就newThing需注册)。
  • 因为崩溃发生在被管理对象的dealloc上,所以我认为一个简单的解决方案是,使model强大而不是weak,当然可以确保它在prepareForReuse:中正确设置为nil。这是否有副作用,我没有意识到?

的错误信息是:

class xxx was deallocated while key value observers were still registered with it

+1

我的事情是,tableview中被重用细胞,也许尝试删除的观察员方法prepareForReuse的uitableviewcell – rafaperez

+0

这不是重点。我知道这个单元格被重用了。当单元格被重用时,模型被设置。看起来KVO正确地管理这种变化。然而,物体在背景中消失,没有细胞的实现。这就是为什么我认为我需要坚持“强”财产而不是弱财。 – thst

回答

1

如果观察到像“model.isDownloading”的路径,然后观察者被登记在model对象,而不是在设定器中self,那是对的吗?

这是一个非常好的问题。据我以前所知,当一个对象注册到KVO时,运行时至少会记录两件事情:1)观察对象和
2)它正在观察的属性的关键路径。

我们知道运行时会覆盖属性的setter以通知观察对象发生变化。

但很明显,运行时还必须跟踪的对象,否则在还有观察者注册的情况下它是否会被释放?

看来,运行时解析keyPath的点,并遵循从接收机参考链(self在你的例子),以保持观察对象的轨迹(self.model

是objC不够聪明如果重新分配modelself.model = newThing要求,removeObserver在分配newThing之前调用model,并且在此之后观察者需要在newThing上注册),则处理观察者更改。

不,它是不够聪明。什么情况是,运行时的子类对象self.model(比方说,这是Model类型)覆盖二传手为isDownloading,例如。现在你self.model对象是NSKVONotifying_Model类型。如果你换出你的self.model指针指向Model类型的新对象时,它不会是运行时创建的相同志愿类的。因此,财产的制定者不会通过添加指令来通知观察员。 所以,是的,你必须在第一对象上删除观察员,并将其添加到第二个对象,即使你使用的是相同的指针变量。

由于碰撞发生在被管理对象的dealloc的,我认为一个简单的解决办法是,使model强,而不是weak,当然要确保,它是在prepareForReuse:正确设置为nil。这是否有副作用,我没有意识到?

这将是正确的,但正如您所知,如果您引用的model对象被换出,您将不得不重新添加观察者。

另一种替代方法(如果您可以更改Model类),则在此环境中将Model类中的引用添加回您的self。然后在您的Model类的init/dealloc,你可以添加自己作为观察员到新的参考。

最后要注意的是,如果你发现自己发送addObserver消息,其中接收器观测对象是same--你可能也只是自己重写制定者。

在你的榜样,你可以只覆盖-setModel做任何你要在你的观察通知处理程序做(-observeValueForKey:::

+0

谢谢,这解释了很多。然而,我必须保留一个强有力的参考,因为如果另一个线程从coredata中删除模型实例,我不知道。该模型实例的发布将触发该异常。如果我不使用强引用,删除后会立即发布,但单元格在屏幕上处于活动状态,并且在表重新加载期间将被删除。强引用保持模型,直到表重新加载完成。 – thst