← Назад к вопросам

В каком методе лучше подписывать под нотификации?

2.2 Middle🔥 222 комментариев
#Архитектура и паттерны

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

🎯 Где подписываться на нотификации в iOS-приложении

Подписка на NotificationCenter — фундаментальная операция в iOS-разработке, и выбор места для её выполнения напрямую влияет на стабильность, читаемость и управляемость кода. В большинстве случаев ответ зависит от типа объекта (UIViewController, UIView, модель) и жизненного цикла уведомления.

✅ Оптимальные места для подписки

1. viewDidLoad() для UIViewController

Наиболее распространённый и безопасный выбор для контроллеров, которые обрабатывают нотификации только при активном экране.

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleNotification(_:)),
            name: .myCustomNotification,
            object: nil
        )
    }
    
    @objc private func handleNotification(_ notification: Notification) {
        // Обработка уведомления
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

Преимущества:

  • Гарантированно вызывается один раз за жизненный цикл контроллера.
  • view уже загружена, можно безопасно обновлять UI.
  • Чёткое разделение инициализации и появления на экране.

2. init() для кастомных UIView или NSObject

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

class MyCustomView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupObservers()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupObservers()
    }
    
    private func setupObservers() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(updateLayout),
            name: UIDevice.orientationDidChangeNotification,
            object: nil
        )
    }
}

3. viewWillAppear() / viewDidAppear() для временных подписок

Если нотификация нужна только когда экран виден, и отписка должна происходить при скрытии.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(keyboardWillShow),
        name: UIResponder.keyboardWillShowNotification,
        object: nil
    )
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(
        self,
        name: UIResponder.keyboardWillShowNotification,
        object: nil
    )
}

❌ Плохие практики и распространённые ошибки

  1. Подписка в viewDidAppear() без отписки в viewDidDisappear() — приводит к множественным подпискам одного объекта, и обработчик будет вызываться несколько раз.
  2. Использование deinit как единственный способ отписки — в Swift с closure-based наблюдением это работает, но для selector-based подписки лучше явная отписка.
  3. Подписка в статических контекстах или синглтонах без механизма отписки — классическая причина утечек памяти.

🆕 Современный подход с iOS 9+

Начиная с iOS 9, система автоматически удаляет наблюдателей, зарегистрированных через NotificationCenter.default.addObserver(_:selector:name:object:), в deinit. Однако это НЕ относится к:

  • Подпискам, возвращающим NSObjectProtocol (токен).
  • Closure-based наблюдению.

Рекомендуемый современный паттерн с токенами:

class ModernViewController: UIViewController {
    private var notificationTokens: [NSObjectProtocol] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let token = NotificationCenter.default.addObserver(
            forName: .myCustomNotification,
            object: nil,
            queue: .main) { [weak self] notification in
                self?.handleNotification(notification)
            }
        notificationTokens.append(token)
    }
    
    deinit {
        // Явное удаление по токенам — самый чистый способ
        notificationTokens.forEach {
            NotificationCenter.default.removeObserver($0)
        }
    }
}

🎖️ Итоговые рекомендации

  1. Для UIViewController — используйте viewDidLoad() как основной метод для постоянных подписок, связанных с жизненным циклом экрана.
  2. Для временных подписок (только когда экран виден) — пара viewWillAppear() / viewWillDisappear().
  3. Всегда предусматривайте механизм отписки — либо через явный вызов removeObserver, либо через сохранение токенов.
  4. Для сложных сценариев рассмотрите использование Combine (iOS 13+) или RxSwift, где управление подписками более декларативно и безопасно.
  5. Избегайте подписки в awakeFromNib() для контроллеров — этот метод может вызываться несколько раз для одного объекта.

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

В каком методе лучше подписывать под нотификации? | PrepBro