iOS NotificationCenter неожиданно сохранил закрытие - PullRequest
0 голосов
/ 27 июня 2019

В документации говорится:

Блок копируется центром уведомлений и (копия) сохраняется до тех пор, пока регистрация наблюдателя не будет удалена.

И он предоставляет примерный пример кода наблюдателя, например так:

let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
var token: NSObjectProtocol?
token = center.addObserverForName("OneTimeNotification", object: nil, queue: mainQueue) { (note) in
    print("Received the notification!")
    center.removeObserver(token!)
}

Теперь я ожидаю, что наблюдатель будет удален при вызове removeObserver(_:), поэтому мой код выглядит следующим образом:

let nc = NotificationCenter.default
var successToken: NSObjectProtocol?
var failureToken: NSObjectProtocol?

successToken = nc.addObserver(
    forName: .ContentLoadSuccess,
    object: nil,
    queue: .main)
{ (_) in
    nc.removeObserver(successToken!)
    nc.removeObserver(failureToken!)

    self.onSuccess(self, .contentData)
}

failureToken = nc.addObserver(
    forName: .ContentLoadFailure,
    object: nil,
    queue: .main)
{ (_) in
    nc.removeObserver(successToken!)
    nc.removeObserver(failureToken!)

    guard case .failed(let error) = ContentRepository.state else {
        GeneralError.invalidState.record()
        return
    }

    self.onFailure(self, .contentData, error)
}

Удивительно, но self сохраняется и не удаляется.

Что происходит?

Ответы [ 2 ]

0 голосов
/ 27 июня 2019

Подтверждено какое-то странное поведение.

Сначала я ставлю точку останова на закрытие наблюдателя успеха, перед тем как наблюдатели удаляются, и печатаю адрес памяти токенов и NotificationCenter.default. Печать NotificationCenter.default показывает зарегистрированных наблюдателей.

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

Printing description of successToken:
▿ Optional<NSObject>
  - some : <__NSObserver: 0x60000384e940>
Printing description of failureToken:
▿ Optional<NSObject>
  - some : <__NSObserver: 0x60000384ea30>

Также подтвердили, что наблюдатели были (предположительно) удалены, напечатав NotificationCenter.default снова после вызова removeObserver(_:).

Затем я покинул контроллер представления и подтвердил, что self в коде кавычек был освобожден.

Наконец, я включил график отладочной памяти, искал адреса памяти и нашел это:

enter image description here

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

Пожалуйста, прокомментируйте, если вы, ребята, думаете, что это ошибка. Согласно документации на NotificationCenter, скорее всего, это ...

0 голосов
/ 27 июня 2019

Вам нужно использовать слабую ссылку для self, например:

let nc = NotificationCenter.default
var successToken: NSObjectProtocol?
var failureToken: NSObjectProtocol?

successToken = nc.addObserver(
    forName: .ContentLoadSuccess,
    object: nil,
    queue: .main)
{[weak self] (_) in

    guard let strongSelf = self else { return }

    nc.removeObserver(successToken!)
    nc.removeObserver(failureToken!)

    strongSelf.onSuccess(strongSelf, .contentData)
}

failureToken = nc.addObserver(
    forName: .ContentLoadFailure,
    object: nil,
    queue: .main)
{[weak self] (_) in

    guard let strongSelf = self else { return }

    nc.removeObserver(successToken!)
    nc.removeObserver(failureToken!)

    guard case .failed(let error) = ContentRepository.state else {
        GeneralError.invalidState.record()
        return
    }

    strongSelf.onFailure(strongSelf, .contentData, error)
}
...