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 — наблюдаем state holder с фиксирана текуща стойност. SharedFlow — конфигурируем hot flow без състояние, подходящ за еднократни събития (навигация, toast). И двата типа са тясно интегрирани с Compose: collectAsState(), collectAsEffect(). StateFlow е задължителен в съвременни Android проекти с Compose.

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

Управление на абонаменти и изтичане на памет

Изтичане на памет (memory leaks) — основният проблем на Observer без правилно управление на абонаменти. Ако абонат (Activity, Fragment, UIViewController) е унищожен, но не се е отписал, издателят продължава да държи референция към него и garbage collector не може да освободи паметта. В 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], тъй като closure силно референцира 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] в closure на handler и извикайте removeObserver() в deinit. Съхранявайте референция към observer (NSObjectProtocol) и я премахнете при унищожаване на обекта. В Combine използвайте AnyCancellable и store(in:) за автоматично отписване при освобождаване на DisposeBag.

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

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

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

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

Каква е разликата между KVO и Combine в iOS?

KVO е остарял ObjC механизъм, изисква @objc dynamic и работи само с класове, наследяващи NSObject. Combine е модерен Swift framework, типобезопасен, с оператори и интеграция със SwiftUI. Combine замества KVO и NotificationCenter. Apple препоръчва Combine за нови проекти, KVO — само за поддръжка на стар код.

Резюме

  • Observer — поведенчески шаблон за уведомяване на абонати за промени
  • iOS NotificationCenter — PubSub реализация с именувани уведомления
  • iOS Combine — реактивен framework с Publisher и Subscriber
  • Android LiveData — lifecycle-aware Observer от Android Architecture Components
  • Android StateFlow — реактивен state holder за Compose и корутини
  • Управление на паметта — задължително отписване за предотвратяване на изтичания
  • Observer vs PubSub — директен абонамент vs посредник за слаба свързаност

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

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също