Observer — ключевые понятия паттерна подписки на изменения

Автор: IT Sectr Опубликовано: 2026-02-17 Время чтения: 7 мин

Observer — поведенческий паттерн, в котором один объект (издатель) уведомляет множество подписчиков об изменениях своего состояния. В мобильной разработке Observer лежит в основе реактивных механик: UI подписывается на изменения данных и автоматически обновляется. Паттерн реализован в NotificationCenter на iOS и LiveData/Flow на Android. Подробнее — в Refactoring Guru: Observer.

Главное

  • Observer — паттерн подписки: один издатель, множество подписчиков
  • NotificationCenter — встроенная реализация Observer в iOS/macOS
  • Flow и LiveData — реактивные реализации Observer в Android
  • Push vs Pull — издатель может отправлять данные или уведомлять о событиях
  • Утечки памяти — подписчики должны отписываться для предотвращения утечек

Что такое Observer: суть паттерна наблюдатель?

Observer (наблюдатель) — поведенческий паттерн GoF, определяющий зависимость «один ко многим» между объектами. Когда один объект (Subject или Observable) меняет состояние, все зависимые объекты (Observers) автоматически уведомляются и обновляются. Паттерн реализует слабую связанность: издатель не знает конкретных классов подписчиков — только то, что они реализуют интерфейс Observer.

Структура Observer включает интерфейс Subject с методами attach(), detach(), notify() и интерфейс Observer с методом update(). ConcreteSubject хранит состояние и список подписчиков. ConcreteObserver реализует update() и реагирует на изменения. В мобильной разработке классическая реализация GoF встречается редко — её заменяют встроенные механизмы: NotificationCenter, Combine, Flow, LiveData, которые реализуют ту же идею с современным API.

Push vs Pull модель — в Push-модели Subject отправляет данные всем подписчикам (NotificationCenter.post). В Pull-модели Subject только уведомляет, а подписчик сам запрашивает данные. Android LiveData использует Push (данные передаются в observe), RxJava/Flow поддерживают обе модели. Выбор зависит от задачи: Push проще для UI-обновлений, Pull эффективнее для больших объёмов данных, которые подписчик может не захотеть получать.

Observer в iOS: NotificationCenter, Combine и KVO

NotificationCenter — встроенный механизм iOS/macOS для реализации Observer. Издатель отправляет Notification через NotificationCenter.default.post(name:, object:, userInfo:). Подписчик регистрируется через addObserver(forName:, queue:, using:). NotificationCenter поддерживает именованные уведомления (Notification.Name) и может передавать любые данные в userInfo. UIKeyboardWillShowNotification, UIApplicationDidEnterBackgroundNotification — системные примеры.

swift
extension Notification.Name {
    static let userDidLogin = Notification.Name("userDidLogin")
}

// Издатель
NotificationCenter.default.post(
    name: .userDidLogin,
    object: nil,
    userInfo: ["userId": "123"]
)

// Подписчик
class ProfileViewModel {
    private var observers: [NSObjectProtocol] = []

    func startObserving() {
        let observer = NotificationCenter.default.addObserver(
            forName: .userDidLogin,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let userId = notification.userInfo?["userId"] as? String else { return }
            // Подписчик реагирует на событие
            self?.loadProfile(userId: userId)
        }
        observers.append(observer)
    }

    func stopObserving() {
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers.removeAll()
    }
}

Combine framework — современная реактивная альтернатива NotificationCenter, появившаяся в iOS 13. Publisher (NotificationCenter, URLSession.Timer) — издатель, Subscriber (sink, assign) — подписчик. Combine добавляет операторы (map, filter, combineLatest) для трансформации потока данных. @Published — property wrapper, автоматически уведомляющий подписчиков об изменениях. В MVVM со SwiftUI Combine заменяет NotificationCenter для связывания ViewModel и View.

KVO (Key-Value Observing) — старый механизм ObjC/Swift для наблюдения за отдельными свойствами объектов. @objc dynamic var name: String — наблюдаемое свойство. observe(.name) — подписка. KVO работает только с @objc-совместимыми классами и наследием ObjC. Apple рекомендует Combine и @Published вместо KVO в новых проектах. KVO остаётся актуальным для UIKit-совместимости в гибридных проектах.

Observer в Android: LiveData, StateFlow и SharedFlow

LiveData — компонент Android Architecture Components для реализации Observer. Observable-класс, который уведомляет подписчиков об изменениях данных. LiveData учитывает жизненный цикл: подписчики (LifecycleOwner) автоматически отписываются при уничтожении. LiveData — Push-модель: данные передаются в observe(). LiveData — базовый строительный блок MVVM в Android до внедрения Jetpack Compose.

kotlin
// ViewModel — издатель
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User?>(null)
    val user: LiveData<User?> = _user

    fun loadUser(id: String) {
        viewModelScope.launch {
            val result = userRepository.getUser(id)
            _user.value = result
        }
    }
}

// Fragment — подписчик
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // Подписчик реагирует на изменения
            userName.text = user?.name
            userEmail.text = user?.email
        }
    }
}

StateFlow и SharedFlow — реактивные типы из Kotlin Coroutines, заменившие LiveData в Jetpack Compose. StateFlow — observable state holder с фиксированным текущим значением. SharedFlow — конфигурируемый hot flow без состояния, подходящий для одноразовых событий (навигация, тосты). Оба типа тесно интегрированы с Compose: collectAsState(), collectAsEffect(). StateFlow обязателен в современных Android-проектах с Compose.

LiveData vs StateFlow — LiveData привязан к Android Lifecycle, StateFlow — платформонезависимый. StateFlow поддерживает coroutines, operators (map, filter) и тестируется без Android-зависимостей. LiveData проще для Java-совместимости. Google рекомендует StateFlow для новых проектов на Kotlin + Compose, LiveData — для поддержки старых проектов или Java-кода.

Управление подписками и утечки памяти

Утечки памяти (memory leaks) — главная проблема Observer без правильного управления подписками. Если подписчик (Activity, Fragment, UIViewController) уничтожен, но не отписан, издатель продолжает держать ссылку на него, и сборщик мусора не может освободить память. В Android LifecycleOwner (Activity/Fragment) должен вызвать removeObserver() или использовать observe(viewLifecycleOwner). В iOS — removeObserver в deinit или disposeBag в Combine.

ПлатформаМеханизм ObserverАвтоматическая отпискаРучная отписка
iOSNotificationCenterНетremoveObserver() в deinit
iOSCombine (sink)Нетstore(in: &bag) — DisposeBag
iOSKVOНетremoveObserver() в deinit
AndroidLiveDataДа (LifecycleOwner)removeObserver() опционально
AndroidStateFlowЧерез viewModelScopecancel() Job при отписке
AndroidRxJavaНетdispose() в CompositeDisposable

Weak reference в подписчиках — при замыкании в блоке подписки используйте [weak self] в Swift и ссылайтесь на lifecycle scope в Kotlin. LiveData автоматически управляет подпиской через LifecycleOwner — подписка активна только когда Lifecycle в состоянии STARTED или RESUMED. StateFlow в Compose использует collectAsState() с учётом lifecycle. NotificationCenter в iOS требует явного [weak self], так как замыкание сильно ссылается на self.

Observer vs Publisher-Subscriber: в чём разница

Observer (GoF) и Publisher-Subscriber (PubSub) — похожие, но разные паттерны. В Observer издатель напрямую уведомляет подписчиков через вызов их методов. Издатель знает о подписчиках (хранит список). В PubSub издатель и подписчик не знают друг о друге — между ними находится посредник (Event Bus, Message Queue, NotificationCenter). Издатель отправляет сообщение на канал, подписчик слушает канал. PubSub — более слабая связанность.

Примеры PubSub в мобильной разработке — NotificationCenter в iOS можно считать PubSub: издатель не знает подписчиков — он просто постит уведомление. EventBus или Otto в Android (устаревшие). SharedFlow с BroadcastChannel — PubSub в мире Kotlin. В распределённых системах PubSub реализуется через RabbitMQ, Kafka, Google PubSub. Для мобильной разработки PubSub полезен при модульной архитектуре, где модули не должны зависеть друг от друга.

Что выбрать — для UI-обновлений (ViewModel → View) используйте Observer (LiveData, StateFlow, @Published). Для межмодульных событий (авторизация, выход из аккаунта, смена темы) — PubSub (SharedFlow, NotificationCenter, EventBus). Observer проще и эффективнее внутри одного экрана, PubSub гибче для глобальных событий, но сложнее в отладке из-за неявных зависимостей.

Часто задаваемые вопросы

Чем StateFlow отличается от LiveData?

StateFlow — платформонезависимый тип из Kotlin Coroutines, LiveData привязан к Android Lifecycle. StateFlow поддерживает coroutines и операторы, тестируется без Android. LiveData автоматически управляет подпиской через LifecycleOwner. Google рекомендует StateFlow для новых проектов на Kotlin + Compose, LiveData — для Java-совместимости.

Как избежать утечек памяти с NotificationCenter?

Используйте [weak self] в замыкании-обработчике и вызывайте removeObserver() в deinit. Храните ссылку на observer (NSObjectProtocol) и удаляйте его при уничтожении объекта. В Combine используйте AnyCancellable и store(in:) для автоматической отписки при освобождении DisposeBag.

Можно ли использовать Observer в SwiftUI без Combine?

Да, SwiftUI поддерживает ObservableObject с @Published и @StateObject/@ObservedObject — это встроенная реализация Observer. @Published автоматически уведомляет View об изменениях. Combine не обязателен: ObservableObject использует objectWillChange Publisher, встроенный в SwiftUI. Combine добавляет операторы для трансформации потоков.

Когда использовать SharedFlow вместо StateFlow?

SharedFlow — для одноразовых событий (навигация, тосты, Snackbar), где не нужно текущее значение. StateFlow — для состояния UI (список данных, прогресс загрузки), где нужен текущий снимок. SharedFlow не имеет value-свойства и не возвращает последнее значение новым подписчикам.

В чём разница между KVO и Combine в iOS?

KVO — устаревший механизм ObjC, требует @objc dynamic и работает только с классами, унаследованными от NSObject. Combine — современный Swift-фреймворк, типобезопасный, с операторами и интеграцией со SwiftUI. Combine заменяет KVO и NotificationCenter. Apple рекомендует Combine для новых проектов, KVO — только для поддержки легаси.

Итоги

  • Observer — поведенческий паттерн для уведомления подписчиков об изменениях
  • iOS NotificationCenter — PubSub-реализация с именованными уведомлениями
  • iOS Combine — реактивный фреймворк с Publisher и Subscriber
  • Android LiveData — lifecycle-aware Observer из Android Architecture Components
  • Android StateFlow — реактивный State holder для Compose и корутин
  • Управление памятью — обязательная отписка для предотвращения утечек
  • Observer vs PubSub — прямая подписка vs посредник для слабой связанности

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также