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

У меня есть 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 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 в ячейке. Вы должны сделать это в контроллере табличного представления, а когда модель изменится, вы должны обновить модель и перезагрузить видимые ячейки.

Ответ 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") } }