Куди видалити спостерігача для NSNotification в Swift?


83

Де я повинен видалити спостерігач NSNotificationв Swift, так viewDidUnloadі dealloc()недоступні?


сьогодні не потрібно видаляти їх вручну, якщо ви не використовуєте стиль блоку.
Fattie

Відповіді:


71

Використовуйте метод нижче, який працює так само, як dealloc.

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

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

Швидкий деініціалізатор


12
Починаючи з iOS 9, відповідно до відповіді нижче, спостерігачі автоматично видаляються для вас, якщо ви не використовуєте блокові.
Crashalot

deinitМетод @Kampai для ViewControllerA не буде викликаний, коли він буде натискати ViewControllerB.
Анірудха Махале,

@AnirudhaMahale - Ні, оскільки ViewControllerA все ще знаходиться в стеку контролера навігації. deinitдля ViewControllerA буде викликано лише тоді, коли його немає в стеку контролера навігації. Наприклад: Перехід на rootViewController (якщо rootViewController не ViewControllerA)
Кампай

@Kampai: Це не спрацює так, ніби ви додаєте спостерігача в контролер перегляду. Існує велика ймовірність того, що він потрапить під цикл збереження і взагалі не дзвонить deinit. Ідеальне місце для дзвінкаfunc viewDidDisappear(_ animated: Bool)
Бхану Бірані

@BhanuBirani: Чи можете ви пояснити будь-який випадок, коли ви згадуєте про "високі шанси"? Ну З мого досвіду я не стикався з жодним.
Кампай

135

Починаючи з 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 лише тоді, коли ви переходите з одного класу в інший програмно, не використовуючи розкадровки?
Кирило

18

У 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
Майте на увазі, що за повільних мережевих умов та певної активності користувача, тобто відходу під час зайнятої дії перегляду, deinit може не викликатися. Я це бачив на тестах.
GordonW

2
@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 Перевірте це
Гай Дахер,

Я думаю, це тому, що у вас є цикл збереження у другому методі, і ви не видалили спостерігача вручну в deallocметоді.
Нік Ков,



0

Стрімкий 5

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

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

    NotificationCenter.default.removeObserver(self)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.