Где удалить наблюдателя для NSNotification в Swift?

83

Где я должен удалить наблюдателя NSNotificationв Swift, поскольку viewDidUnloadи dealloc()недоступны?

Климент Джозеф
источник
в настоящее время вам не нужно удалять их вручную, если вы не используете блочный стиль.
Fattie

Ответы:

71

Используйте метод ниже, который работает так же, как dealloc.

deinit {
    // Release all resources
    // perform the deinitialization
}

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

Быстрый деинициализатор

Кампай
источник
13
Начиная с iOS 9, согласно ответу ниже, наблюдатели автоматически удаляются за вас, если вы не используете блочные.
Crashalot
deinitМетод @Kampai для ViewControllerA не будет вызываться, когда он отправит ViewControllerB.
Anirudha Mahale
@AnirudhaMahale - Нет, потому что ViewControllerA все еще находится в стеке контроллера навигации. deinitдля ViewControllerA будет вызываться только тогда, когда его нет в стеке контроллера навигации. Например: переключение на rootViewController (если rootViewController не ViewControllerA)
Кампай,
@Kampai: это не будет работать, как если бы вы добавляли наблюдателя в контроллер представления. Есть высокие шансы, что он попадет в цикл удержания и вообще не вызовет deinit. Идеальное место для звонкаfunc viewDidDisappear(_ animated: Bool)
Бхану Бирани
@BhanuBirani: Не могли бы вы объяснить любой случай, когда вы упоминаете о «высоких шансах». Ну, по моему опыту, я ни с чем не сталкивался.
Kampai
136

Начиная с iOS 9 (и OS X 10.11), вам не нужно удалять наблюдателей. самостоятельно, если вы не используете наблюдателей на основе блоков. Система сделает это за вас, поскольку использует слабые обнуления для наблюдателей там, где это возможно.

И если вы используете наблюдателей на основе блоков, убедитесь, что вы слабо захватываете себя, используя [weak self]в списке захвата закрытия, и удалите наблюдателя в deinitметоде. Если вы не используете слабую ссылку на себя, deinitметод (и, следовательно, удаление этого наблюдателя) никогда не будет вызван, поскольку Центр уведомлений будет хранить сильную ссылку на него неопределенно долго.

Дополнительную информацию можно найти в Примечаниях к выпуску Foundation для OS X v10.11 и iOS 9 .

Если наблюдатель может быть сохранен как слабая обнуляемая ссылка, базовое хранилище сохранит наблюдателя как обнуляемую слабую ссылку, в качестве альтернативы, если объект не может быть сохранен в слабом режиме (т.е. он имеет настраиваемый механизм сохранения / освобождения, который предотвратит выполнение из-за возможности слабого хранения объекта) он сохранит объект как неслабую ссылку обнуления. Это означает, что наблюдатели не обязаны отменять регистрацию в их методе освобождения.

Наблюдатели на основе блоков с помощью метода - [NSNotificationCenter addObserverForName: object: queue: usingBlock] по-прежнему необходимо отменить регистрацию, когда они больше не используются, поскольку система по-прежнему имеет сильную ссылку на этих наблюдателей.

Никола Миличевич
источник
1
Мне любопытно, работает ли это с делегатами? Я видел в iOS8, что делегаты занимают память, а не сохраняют. Я писал delegate = nilв dealloc()методе. Теперь он работает так же?
Кампай
1
Как правило, делегаты следует объявлять как слабые ссылки, и никакой другой работы не требуется.
Никола Миличевич
Поскольку вы специально упомянули, что это не работает для блочных наблюдателей: не могли бы вы объяснить, почему? Есть ли способ обойти это? например [слабое я]
Филипп Джахода
62

Вы можете использовать три метода:

  1. после popViewController, назад navigationControllerили dismissViewControllerAnimated:

    deinit {
        print("Remove NotificationCenter Deinit")
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  2. viewDidDisappear, удалите после того, как это уже следующий контроллер представления:

    override func viewDidDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
  3. viewWillDisappear - перед открытием следующего вида:

    override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    

Синтаксис Swift 3.0:

NotificationCenter.default.removeObserver(self)
Пабло Руан
источник
1
Я думаю, что deinit - лучший вариант.
Гленн Посадас
В iOS 9, по словам @Nikola Milicevic, наблюдатели автоматически удаляются за вас, если вы не используете блочные.
Crashalot
Помешает ли удаление наблюдателей, когда вы покидаете контроллер, необходимость иметь наблюдателей? И работает ли deinit только тогда, когда вы программно переходите от одного класса к другому без использования раскадровки?
Кирилл
21

В Swift 4.2 это один из способов удаления наблюдателя.

deinit {
    NotificationCenter.default.removeObserver(self, name: Notification.Name.Identifier, object: nil)
}

настроить уведомление addObserver в классе viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(didReceivedItemDetail), name: Notification.Name.Identifier, object: nil)
}
Ашим Дахал
источник
2
Имейте в виду, что в условиях медленной сети и определенной активности пользователя, например, при переходе во время активного просмотра, деинициализация действия может не выполняться. Я видел это на тестах.
GordonW
3
@GordonW, если ваш метод deinit не вызывается в конце жизненного цикла вашего контроллера представления, то в этом классе есть проблема с памятью.
Ашим Дахал
4

Также хочу отметить, что вам следует использовать этот метод:

func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)

Вместо

func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol

Последний не удалит наблюдателя (недавно столкнулся с этой проблемой). Первый удалит наблюдателя, если вы используете iOS9.

Гай Дахер
источник
Когда первый удаляет наблюдателя?
Shubham
@Shubham Проверьте это
Гай Дахер
Я думаю, это потому, что у вас есть цикл сохранения во втором методе и вы не удалили наблюдателя вручную в deallocметоде.
Nik Kov
2
deinit {
    NotificationCenter.default.removeObserver(self)
}
Павел Молодкин
источник
1

Swift 5

У меня есть приложение для чата, поэтому всякий раз, когда я перехожу из своего ChatLogViewController к другому viewController, а затем возвращаюсь, у меня есть 1 дополнительный наблюдатель моего уведомления с клавиатуры. Чтобы удалить это, я удаляю всех наблюдателей, когда меняю свой viewController или исчезаю из моего chatLogViewController .

override func viewDidDisappear(_ animated: Bool) {    
    super.viewDidDisappear(animated)

    NotificationCenter.default.removeObserver(self)
}
Симран Сингх
источник
0

Также хорошо, если вы добавите своего наблюдателя viewWillAppear()и удалите их вviewWillDisappear()

Mayank Khare
источник