Контроллер вида dealloc не вызывается при использовании метода блока кода NSNotificationCenter с ARC


Когда я использую -addObserverForName: object: queue: usingBlock: для NSNotificationCenter в методе -viewDidLoad: моего контроллера вида, метод -dealloc не вызывается.

(Когда я удаляю -addObserverForName: object: queue: usingBlock:, -dealloc снова звонят.)

Использование -addObserver: selector: name: object:, похоже, не имеет этой проблемы. Что я делаю не так? (Мой проект использует ARC.)

Ниже приведен пример моей реализации, на случай, если я делаю что-то не так:

[[NSNotificationCenter defaultCenter] addObserverForName:@"Update result"
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification *note) {
                                                  updateResult = YES;
                                              }];

Заранее спасибо за любую помощь.

Я попытался добавить следующее (К no avail):

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    if ([self isMovingFromParentViewController]) {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
}
3 8

3 ответа:

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

Другими словами, у вас есть цикл удержания. Объект сохраняет блок, а блок сохраняет объект.

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

Перед блоком уведомлений добавьте следующее:

__unsafe_unretained YouObjectClass *weakSelf = self;

Или (в случае, если вы находитесь на iOS5 и выше)

__weak YouObjectClass *weakSelf = self;

Затем, внутри этого блока, ссылайтесь на объект через эту новую слабую ссылку:

[[NSNotificationCenter defaultCenter] addObserverForName:@"Update result"
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification *note) {
                                                  weakSelf.updateResult = YES;
                                              }];

Пожалуйста, обратите внимание, что удерживающие циклы сами по себе не так уж плохи. Иногда вы действительно хотите, чтобы они произошли. Но это те случаи, когда вы уверены, что цикл будет прерван через определенное время (например, анимационные блоки). Цикл прерывается, как только блок выполнен и удален из стека.

Это очень вероятно, потому что у вас есть цикл удержания.

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

Вы должны активировать предупреждение -Warc-retain-cycles, которое предупредит вас о таких проблемах.

Таким образом, в вашем случае вы используете переменную updateResult, которая, как я предполагаю, является переменной экземпляра, и это неявно сохранить self. Вместо этого вы должны использовать временную слабую переменную для представления себя и использовать ее в своем блоке, чтобы она не была сохранена, и вы разорвете цикл сохранения.
__block __weak typeof(self) weakSelf = self; // weak reference to self, unretained by the block
[[NSNotificationCenter defaultCenter] addObserverForName:@"Update result"
                                              object:nil
                                               queue:nil
                                          usingBlock:^(NSNotification *note) {
                                              // Use weakSelf explicitly to avoid the implicit usage of self and thus the retain cycle
                                              weakSelf->updateResult = YES;
                                          }];

Это не цикл удержания.

NSNotificationCenter держите блок, блок-это удержание self. Поскольку [NSNotificationCenter defaultCenter] является синглетом, живущим во всем жизненном цикле приложения, поэтому он содержит self косвенный.