【问题标题】:keyValue observer from cell to managed object从单元到托管对象的 keyValue 观察者
【发布时间】:2023-03-22 08:03:01
【问题描述】:

我在这里查看一个表格视图单元格,我找到了这段代码:

- (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 方法中被移除。 modelweak 属性,接收托管对象(核心数据)。

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

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

我有一些问题:

  • 如果观察到类似“model.isDownloading”的路径,那么观察者注册在model 对象中,而不是在self 的setter 中,对吗?
  • 如果model 被重新分配(self.model = newThing 要求,removeObserver 在分配model 之前调用model,并且观察者需要在newThing 上注册,objC 是否足够聪明以处理观察者的变化之后)。
  • 由于崩溃发生在托管对象的dealloc上,我认为一个简单的解决方案是,使model强而不是weak,当然要确保它正确设置为nil prepareForReuse:。这是否有我没有意识到的副作用?

错误信息是:

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

【问题讨论】:

  • 我认为 tableview 正在重用单元格,也许尝试删除 uitableviewcell 的 prepareForReuse 方法上的观察者
  • 这不是重点。我知道细胞被重复使用了。当单元被重用时,模型被设置。看起来 KVO 正确地管理了这个变化。然而,对象消失在背景中,而细胞没有意识到。这就是为什么我认为我需要使用 strong 属性而不是弱属性来保留引用。

标签: ios objective-c uitableview key-value-observing


【解决方案1】:

如果观察到类似“model.isDownloading”的路径,那么观察者注册在model对象中,而不是在self的setter中,对吗?

这是一个很好的问题。据我之前所知,当一个对象向 KVO 注册时,运行时至少会跟踪两件事
1) 正在观察和
的对象 2) 它正在观察的属性的键路径。

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

但很明显,运行时还必须跟踪被被观察的对象,否则它怎么知道它是否正在被释放而仍然有观察者注册呢?

运行时似乎将keyPath 解析为点,并遵循来自接收器的引用链(在您的示例中为self),以跟踪观察到的对象(self.model

如果model 被重新分配(self.model = newThing 要求,removeObserver 在分配model 之前调用model,并且观察者需要注册,则 objC 是否足够聪明以处理观察者的变化newThing 之后)。

不,它不够聪明。例如,运行时将您的self.model 对象(假设它的类型为Model)子类化以覆盖isDownloading 的设置器。现在您的self.model 对象的类型为NSKVONotifying_Model。如果您要交换您的 self.model 指针以指向 Model 类型的新对象,那么它将与运行时创建的 KVO 类不同。因此,该属性的设置器不会添加通知观察者的指令。 所以是的,即使您使用相同的指针变量,您也必须删除第一个对象上的观察者并将其添加到第二个对象。

由于崩溃发生在托管对象的 dealloc 上,我认为一个简单的解决方案是使 modelstrong 而不是 weak,当然要确保它正确设置为 @ 987654341@ 在prepareForReuse:。这是否有我没有意识到的副作用?

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

另一种选择(如果您可以更改Model 类)是在此上下文中将Model 类中的引用添加回您的self。然后在Model 类的init/dealloc 中,您可以将自己作为观察者添加到您的新引用中。

最后一点是,如果你发现自己发送了一个addObserver 消息,而接收者观察者对象是相同的——你也可以自己重写设置器。

在您的示例中,您可以直接覆盖 -setModel 来执行您在观察通知处理程序 (-observeValueForKey:::) 中要执行的任何操作

【讨论】:

  • 谢谢,这解释了很多。然而,我必须保留一个强有力的参考,因为如果另一个线程从 coredata 中删除模型实例,我不会得到通知。该模型实例的释放将触发异常。如果我不使用强引用,则删除后会立即释放,但单元格在屏幕上处于活动状态,并且会在重新加载表时被删除。强引用保留模型,直到表重新加载完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
相关资源
最近更新 更多