Экземпляр был освобожден, в то время как наблюдатели ключевых значений все еще регистрировались в нем


У меня есть 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");
}

Все ячейки освобождаются перед всеми моделями, но все же я получите эту ошибку. Кроме того, я не знаю, как установить точку останова, упомянутую в ошибке.

5 15

5 ответов:

Это не сработает, потому что клетки используются повторно. Поэтому, когда ячейка исчезает с экрана, она не освобождается, а отправляется в пул повторного использования.

Вы не должны регистрировать уведомления и кво в ячейке. Вместо этого вы должны сделать это в контроллере табличного представления, и когда модель изменяется, вы должны обновить модель и перезагрузить видимые ячейки.

Я нашел ответ. Я не могу удалить поток, кто-то ответил :) Может быть, это будет полезно для кого-то.

Проблема заключается в том, что 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];

Основываясь на принятом ответе (который является правильным), Вы можете решить его, удалив наблюдателя по методу " prepareForReuse" ячейки.

Этот метод будет вызван перед повторным использованием ячейки при прокрутке и т. д.. таким образом, у вас не будет никаких проблем.

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

Существует вероятность, что ваш контроллер вида не вызывает метод dealloc, потому что его ссылка может быть удержана кем-то, и ваш метод dealloc не вызывается. Вы можете удалить наблюдателя в вашем viewDidUnload: или viewWillDisappear: методе или вы можете проследить ваш контроллер в инструмент для любого сохранения

Лучшее место для этого для ячеек и многоразовых представлений находится в 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") } }