从单元格到托管对象的键值观察器
keyValue observer from cell to managed object
我正在查看此处的 table 视图单元格,我发现了这段代码:
- (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
属性,接收一个托管对象(核心数据)。
我收到虚假崩溃,告诉我管理对象已删除,但仍有观察者注册。
错误发生的原因对我来说很清楚:对象在后台某处被删除,但仍链接到 table 视图的单元格中。由于单元格上的 dealloc
在应用程序的生命周期内基本上从未被调用过,因此从未真正删除观察者。由于对核心数据对象的引用是 weak
,它将在后台静默解除分配 - 至少尝试这样做。这失败了,因为模型仍然被观察到。
我有一些问题:
- 如果观察到像 "model.isDownloading" 这样的路径,那么观察者是在
model
对象中注册的,而不是在 self
中的 setter 中注册的,对吗?
- 如果
model
被重新分配,objC 是否足够聪明来处理观察者变化(self.model = newThing
要求,在分配 [=22= 之前在 model
上调用 removeObserver
],观察员需要在 newThing
之后注册。
- 由于崩溃发生在托管对象的 dealloc 上,我认为一个简单的解决方案是使
model
强而不是 weak
,当然要确保它是正确的在 prepareForReuse:
中设置为 nil
。这是否有副作用,我还没有意识到?
错误信息是:
class xxx was deallocated while key value observers were still registered with it
如果观察到像"model.isDownloading"这样的路径,那么观察者是在model
对象中注册的,而不是在self
中的setter中,对吗?
这是一个很好的问题。据我之前所知,当一个对象注册到 KVO
时,运行时至少会跟踪两件事
1) 是观察的对象和
2) 它正在观察的 属性 的键路径。
我们知道运行时会覆盖 属性 的 setter 以通知对象观察到变化。
但显然运行时还必须跟踪被观察的对象,否则它怎么知道它是否在仍有观察者注册的情况下被释放?
运行时似乎解析了 keyPath
的点,并遵循来自接收器的引用链(在您的示例中为 self
),以跟踪观察到的对象(self.model
)
如果 model
被重新分配,objC 是否足够聪明来处理观察者变化(self.model = newThing
要求 removeObserver
在 model
之前被调用分配 newThing
,然后观察者需要在 newThing
注册。
不,它不够聪明。例如,运行时 subclasses 您的 self.model
对象(假设它的类型为 Model
)覆盖 isDownloading
的 setter。现在您的 self.model
对象的类型是 NSKVONotifying_Model
。如果您要换出 self.model
指针以指向类型为 Model
的新对象,它将与运行时创建的 KVO class 不同。因此 属性 的 setter 不会添加通知观察者的指令。
所以是的,你必须删除第一个对象上的观察者并将其添加到第二个对象,即使你使用的是相同的指针变量。
由于崩溃发生在托管对象的 dealloc 上,我认为一个简单的解决方案是,使 model
强而不是 weak
,当然要确保, 它在 prepareForReuse:
中正确设置为 nil
。这是否有副作用,我还没有意识到?
这是正确的,但如您所知,如果您引用的 model
对象被换出,您将不得不 re-add 观察者。
另一种选择(如果您可以更改 Model
class),是在 Model
class 中添加一个引用回到您的 self
在这种情况下。然后在你的 Model
class 的 init
/dealloc
中,你可以将自己作为观察者添加到你的新引用中。
最后要注意的是,如果您发现自己发送了一个 addObserver
消息,而接收者 和 观察者对象是相同的——您也可以覆盖setter 你自己。
在您的示例中,您可以覆盖 -setModel
以执行您要在观察通知处理程序中执行的任何操作 (-observeValueForKey:::
)
我正在查看此处的 table 视图单元格,我发现了这段代码:
- (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
属性,接收一个托管对象(核心数据)。
我收到虚假崩溃,告诉我管理对象已删除,但仍有观察者注册。
错误发生的原因对我来说很清楚:对象在后台某处被删除,但仍链接到 table 视图的单元格中。由于单元格上的 dealloc
在应用程序的生命周期内基本上从未被调用过,因此从未真正删除观察者。由于对核心数据对象的引用是 weak
,它将在后台静默解除分配 - 至少尝试这样做。这失败了,因为模型仍然被观察到。
我有一些问题:
- 如果观察到像 "model.isDownloading" 这样的路径,那么观察者是在
model
对象中注册的,而不是在self
中的 setter 中注册的,对吗? - 如果
model
被重新分配,objC 是否足够聪明来处理观察者变化(self.model = newThing
要求,在分配 [=22= 之前在model
上调用removeObserver
],观察员需要在newThing
之后注册。 - 由于崩溃发生在托管对象的 dealloc 上,我认为一个简单的解决方案是使
model
强而不是weak
,当然要确保它是正确的在prepareForReuse:
中设置为nil
。这是否有副作用,我还没有意识到?
错误信息是:
class xxx was deallocated while key value observers were still registered with it
如果观察到像"model.isDownloading"这样的路径,那么观察者是在model
对象中注册的,而不是在self
中的setter中,对吗?
这是一个很好的问题。据我之前所知,当一个对象注册到 KVO
时,运行时至少会跟踪两件事
1) 是观察的对象和
2) 它正在观察的 属性 的键路径。
我们知道运行时会覆盖 属性 的 setter 以通知对象观察到变化。
但显然运行时还必须跟踪被观察的对象,否则它怎么知道它是否在仍有观察者注册的情况下被释放?
运行时似乎解析了 keyPath
的点,并遵循来自接收器的引用链(在您的示例中为 self
),以跟踪观察到的对象(self.model
)
如果 model
被重新分配,objC 是否足够聪明来处理观察者变化(self.model = newThing
要求 removeObserver
在 model
之前被调用分配 newThing
,然后观察者需要在 newThing
注册。
不,它不够聪明。例如,运行时 subclasses 您的 self.model
对象(假设它的类型为 Model
)覆盖 isDownloading
的 setter。现在您的 self.model
对象的类型是 NSKVONotifying_Model
。如果您要换出 self.model
指针以指向类型为 Model
的新对象,它将与运行时创建的 KVO class 不同。因此 属性 的 setter 不会添加通知观察者的指令。
所以是的,你必须删除第一个对象上的观察者并将其添加到第二个对象,即使你使用的是相同的指针变量。
由于崩溃发生在托管对象的 dealloc 上,我认为一个简单的解决方案是,使 model
强而不是 weak
,当然要确保, 它在 prepareForReuse:
中正确设置为 nil
。这是否有副作用,我还没有意识到?
这是正确的,但如您所知,如果您引用的 model
对象被换出,您将不得不 re-add 观察者。
另一种选择(如果您可以更改 Model
class),是在 Model
class 中添加一个引用回到您的 self
在这种情况下。然后在你的 Model
class 的 init
/dealloc
中,你可以将自己作为观察者添加到你的新引用中。
最后要注意的是,如果您发现自己发送了一个 addObserver
消息,而接收者 和 观察者对象是相同的——您也可以覆盖setter 你自己。
在您的示例中,您可以覆盖 -setModel
以执行您要在观察通知处理程序中执行的任何操作 (-observeValueForKey:::
)