实例被解除分配,而键值观察者仍向其注册

Posted

技术标签:

【中文标题】实例被解除分配,而键值观察者仍向其注册【英文标题】:Instance was deallocated while key value observers were still registered with it 【发布时间】:2014-07-31 10:46:42 【问题描述】:

我有一个 UITableView。

在这里我得到了不同的单元格。每个单元格都有一个模型。使用 KVO 和 NotificationCenter,单元会监听模型的变化。当我离开 ViewController 我得到这个错误:

An instance 0x109564200 of class Model was deallocated while key value observers were still registered with it. 
Observation info was leaked, and may even become mistakenly attached to some other object. 
Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x109429cc0> (
<NSKeyValueObservance 0x109429c50: Observer: 0x10942d1c0, Key path: name, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x10968fa00>
)

在单元格中,我在设置/更改模型属性时执行此操作:

[_model addObserver:self
         forKeyPath:@"name"
            options:0
            context:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(modelIsInvalid:)
                                             name:@"modelIsInvalid"
                                           object:_model];

然后在单元格的dealloc中:

- (void)dealloc

    NSLog(@"DEALLOC CELL");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_model removeObserver:self forKeyPath:@"name"];

在模型中,我还会检查它何时被释放:

- (void)dealloc

    NSLog(@"DEALLOC MODEL");

在所有模型之前释放所有单元格,但我仍然收到此错误。另外我不确定如何设置错误中提到的断点。

【问题讨论】:

【参考方案1】:

它不起作用,因为单元格正在被重复使用。因此,当单元格离开屏幕时,它并没有被释放,而是进入重用池。

您不应在单元格中注册通知和 KVO。您应该在表格视图控制器中执行此操作,并且当模型更改时您应该更新模型并重新加载可见单元格。

【讨论】:

这就是解决它的方法,请参阅我的答案了解我是如何做到的(接近你的) 我不同意“您不应该在单元格中注册通知和 KVO”。使用正确的配方,从细胞中观察会更方便,而且我一直都在这样做,没有任何问题。 @Johannes 找到了正确的方法。 @Mazyod 你能演示一下“正确的食谱”吗?最好快速;) @natecraft1 我实际上改变了主意,这个答案是要走的路。如果需要,让您的单元格观察其模型,表格视图也可以观察模型的变化。严格来说,KVO 可能不适合 cell,委托和回调会更好。 @Mazyod 非常感谢您的快速回复。老实说,虽然我对 ios 很陌生,并不完全了解如何将其转换为代码。我的猜测是在 TableViewController 中的 cellForRowAtIndexPath 方法中,我向与该特定单元格关联的对象添加了一个观察者。是对的吗?如果是这样,我什么时候从该对象中删除观察者?【参考方案2】:

我找到了答案。我不能删除线程,有人已经回答了:)也许它对某人有用。

问题在于 UITableView 将出列之前使用的相同单元格,向下更长的行(当滚动足够远时变得可见)。

在我现在拥有的二传手中:

// Before we set new model
if (_model) 
    [_model removeObserver:self forKeyPath:@"name"];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"modelIsInvalid" object:_model];


_model = model;

[_model addObserver:self
         forKeyPath:@"name"
            options:0
            context:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(modelIsInvalid:)
                                             name:@"modelIsInvalid"
                                           object:_model];

【讨论】:

【参考方案3】:

根据接受的答案(这是正确的),您可以通过在单元格的“prepareForReuse”方法上移除观察者来解决它。

在滚动等重用单元格之前将调用该方法。因此您不会有任何问题。

- (void)prepareForReuse
    [_model removeObserver:self forKeyPath:@"name"];

【讨论】:

从单元格中移除观察者的绝佳答案【参考方案4】:

您的视图控制器可能没有调用 dealloc 方法,因为它的引用可能被某人持有并且您的 dealloc 方法没有被调用。您可以在 viewDidUnload:viewWillDisappear: 方法中删除观察者,或者您可以将控制器跟踪到仪器以进行任何保留

【讨论】:

【参考方案5】:

为单元格和可重用视图执行此操作的最佳位置是willMove(toSuperiew)

override func willMove(toSuperview newSuperview: UIView?) if newSuperview == nil // check for nil means this will be removed from superview self.collectionView?.removeObserver(self, forKeyPath: "contentSize")

【讨论】:

以上是关于实例被解除分配,而键值观察者仍向其注册的主要内容,如果未能解决你的问题,请参考以下文章

AVAudioPlayer 被解除分配,而键值观察者仍向其注册

iOS 5 AVPlayerItem 的一个实例被释放

facebook ios sdk 日志:奇怪的消息

类 -(MyAnnotation 注释)被释放

如何用键盘解决这个奇怪的崩溃

Objective-C 删除某个通知的所有观察者