Observer — egy viselkedési minta, amelyben egy objektum (kiadó) értesíti a számos előfizetőt az állapotváltozásairól. A mobil fejlesztésben az Observer a reaktív mechanikák alapját képezi: az UI előfizet az adatváltozásokra és automatikusan frissül. A minta a NotificationCenter-ben iOS-en és a LiveData/Flow-ban Android-on van implementálva. Bővebben — a Refactoring Guru: Observer oldalon.
Főbb pontok
Observer (megfigyelő) — egy GoF viselkedési minta, amely «egy-többöz» függőséget határoz meg objektumok között. Amikor egy objektum (Subject vagy Observable) megváltoztatja az állapotát, az összes függő objektum (Observers) automatikusan értesül és frissül. A minta gyenge csatolást valósít meg: a kiadó nem ismeri az előfizetők konkrét osztályait — csak azt, hogy implementálják az Observer interfászt.
Observer struktúra magában foglalja a Subject interfászt az attach(), detach(), notify() metódusokkal és az Observer interfászt az update() metódussal. A ConcreteSubject tárolja az állapotot és az előfizetők listáját. A ConcreteObserver implementálja az update()-et és reagál a változásokra. A mobil fejlesztésben a klasszikus GoF implementáció ritkán fordul elő — beépített mechanizmusok váltják fel: NotificationCenter, Combine, Flow, LiveData, amelyek ugyanazt az ötletet valósítják meg modern API-val.
Push vs Pull modell — a Push modellben a Subject adatokat küld az összes előfizetőnek (NotificationCenter.post). A Pull modellben a Subject csak értesít, és az előfizető maga kéri le az adatokat. Az Android LiveData Push-t használ (az adatok az observe()-ban kerülnek átadásra), az RxJava/Flow mindkét modellt támogatja. A választás a feladattól függ: a Push egyszerűbb az UI-frissítésekhez, a Pull hatékonyabb nagy adatmennyiségek esetén, amelyeket az előfizető esetleg nem akar megkapni.
NotificationCenter — az Observer implementálására szolgáló beépített iOS/macOS mechanizmus. A kiadó Notification-t küld a NotificationCenter.default.post(name:, object:, userInfo:) segítségével. Az előfizető az addObserver(forName:, queue:, using:) segítségével regisztrál. A NotificationCenter támogatja a névvel ellátott értesítéseket (Notification.Name) és bármilyen adatot átadhat a userInfo-ban. UIKeyboardWillShowNotification, UIApplicationDidEnterBackgroundNotification — rendszerpéldák.
extension Notification.Name {
static let userDidLogin = Notification.Name("userDidLogin")
}
// Kiadó
NotificationCenter.default.post(
name: .userDidLogin,
object: nil,
userInfo: ["userId": "123"]
)
// Előfizető
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 }
// Az előfizető reagál az eseményre
self?.loadProfile(userId: userId)
}
observers.append(observer)
}
func stopObserving() {
observers.forEach { NotificationCenter.default.removeObserver($0) }
observers.removeAll()
}
}
Combine framework — egy modern reaktív alternatíva a NotificationCenter számára, iOS 13-tól elérhető. Publisher (NotificationCenter, URLSession.Timer) — kiadó, Subscriber (sink, assign) — előfizető. A Combine operátorokat (map, filter, combineLatest) ad hozzá az adatfolyam transzformálásához. @Published — property wrapper, amely automatikusan értesíti az előfizetőket a változásokról. Az MVVM-ben SwiftUI-val a Combine helyettesíti a NotificationCenter-t a ViewModel és View összekapcsolásához.
KVO (Key-Value Observing) — egy régi ObjC/Swift mechanizmus objektumok egyedi tulajdonságainak megfigyelésére. @objc dynamic var name: String — megfigyelt tulajdonság. observe(.name) — előfizetés. A KVO csak @objc-kompatibilis osztályokkal és ObjC öröklődéssel működik. Az Apple a KVO helyett a Combine és @Published használatát ajánlja új projektekben. A KVO továbbra is releváns a UIKit kompatibilitás miatt hibrid projektekben.
LiveData — az Observer implementálására szolgáló Android Architecture Components komponens. Egy Observable osztály, amely értesíti az előfizetőket az adatváltozásokról. A LiveData figyelembe veszi az életciklust: az előfizetők (LifecycleOwner) automatikusan leiratkoznak a megsemmisüléskor. A LiveData Push modell: az adatok az observe()-ban kerülnek átadásra. A LiveData az MVVM alapvető építőeleme Android-ban a Jetpack Compose bevezetése előtt.
// ViewModel — kiadó
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 — előfizető
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.user.observe(viewLifecycleOwner) { user ->
// Az előfizető reagál a változásokra
userName.text = user?.name
userEmail.text = user?.email
}
}
}
StateFlow és SharedFlow — reaktív típusok a Kotlin Coroutines-ból, amelyek felváltották a LiveData-t a Jetpack Compose-ban. StateFlow — megfigyelhető állapottartó (state holder) rögzített aktuális értékkel. SharedFlow — konfigurálható hot flow állapot nélkül, alkalmas egyszeri eseményekre (navigáció, toastok). Mindkét típus szorosan integrálódik a Compose-szal: collectAsState(), collectAsEffect(). A StateFlow kötelező a modern Android projektekben Compose-szal.
LiveData vs StateFlow — a LiveData az Android Lifecycle-hez kötött, a StateFlow platformfüggetlen. A StateFlow támogatja a coroutines-t, operátorokat (map, filter) és Android függőségek nélkül tesztelhető. A LiveData egyszerűbb a Java kompatibilitás szempontjából. A Google a StateFlow-t ajánlja új, Kotlin + Compose projektekhez, a LiveData-t — régi projektek vagy Java kód támogatására.
Memóriaszivárgás (memory leaks) — az Observer fő problémája a megfelelő előfizetéskezelés nélkül. Ha egy előfizető (Activity, Fragment, UIViewController) megsemmisült, de nem iratkozott le, a kiadó továbbra is tartja a referenciát, és a garbage collector nem tudja felszabadítani a memóriát. Android-ban a LifecycleOwner-nek (Activity/Fragment) meg kell hívnia a removeObserver()-t, vagy használnia kell az observe(viewLifecycleOwner)-t. iOS-ben — removeObserver a deinit-ben vagy disposeBag a Combine-ban.
| Platform | Observer mechanizmus | Automatikus leiratkozás | Kézi leiratkozás |
|---|---|---|---|
| iOS | NotificationCenter | Nem | removeObserver() a deinit-ben |
| iOS | Combine (sink) | Nem | store(in: &bag) — DisposeBag |
| iOS | KVO | Nem | removeObserver() a deinit-ben |
| Android | LiveData | Igen (LifecycleOwner) | removeObserver() opcionális |
| Android | StateFlow | viewModelScope-on keresztül | cancel() Job leiratkozáskor |
| Android | RxJava | Nem | dispose() a CompositeDisposable-ban |
Gyenge referencia (weak reference) az előfizetőkben — az előfizetési blokkban használjon [weak self]-et Swift-ben és hivatkozzon lifecycle scope-ra Kotlin-ban. A LiveData automatikusan kezeli az előfizetést a LifecycleOwner segítségével — az előfizetés csak akkor aktív, amikor a Lifecycle STARTED vagy RESUMED állapotban van. A StateFlow a Compose-ban a collectAsState()-t használja a lifecycle figyelembevételével. A NotificationCenter iOS-ben explicit [weak self]-et igényel, mivel a closure erősen hivatkozik a self-re.
Observer (GoF) és Publisher-Subscriber (PubSub) — hasonló, de különböző minták. Az Observer-ben a kiadó közvetlenül értesíti az előfizetőket a metódusaik meghívásával. A kiadó tud az előfizetőkről (listát tárol). A PubSub-ban a kiadó és az előfizető nem tud egymásról — egy közvetítő (Event Bus, Message Queue, NotificationCenter) van közöttük. A kiadó üzenetet küld egy csatornára, az előfizető hallgatja a csatornát. A PubSub gyengébb csatolást biztosít.
PubSub példák mobil fejlesztésben — a NotificationCenter iOS-ben PubSub-nak tekinthető: a kiadó nem ismeri az előfizetőket — csak közzéteszi az értesítést. EventBus vagy Otto Android-ban (elavult). SharedFlow BroadcastChannel-nel — PubSub a Kotlin világában. Elosztott rendszerekben a PubSub RabbitMQ, Kafka, Google PubSub segítségével valósítható meg. Mobil fejlesztésben a PubSub moduláris architektúrában hasznos, ahol a modulok nem függhetnek egymástól.
Mit válasszunk — UI-frissítésekhez (ViewModel → View) használjon Observer-t (LiveData, StateFlow, @Published). Modulok közötti eseményekhez (hitelesítés, kijelentkezés, téma váltás) — PubSub-ot (SharedFlow, NotificationCenter, EventBus). Az Observer egyszerűbb és hatékonyabb egy képernyőn belül, a PubSub rugalmasabb a globális eseményekhez, de nehezebben debugolható az implicit függőségek miatt.
Gyakran Ismételt Kérdések
A StateFlow egy platformfüggetlen típus a Kotlin Coroutines-ból, a LiveData az Android Lifecycle-hez kötött. A StateFlow támogatja a coroutines-t és operátorokat, Android nélkül tesztelhető. A LiveData automatikusan kezeli az előfizetést a LifecycleOwner segítségével. A Google a StateFlow-t ajánlja új, Kotlin + Compose projektekhez, a LiveData-t — Java kompatibilitáshoz.
Használjon [weak self]-et a handler closure-ban és hívja meg a removeObserver()-t a deinit-ben. Tároljon referenciát az observer-re (NSObjectProtocol) és távolítsa el az objektum megsemmisülésekor. A Combine-ban használjon AnyCancellable-t és store(in:)-t az automatikus leiratkozáshoz a DisposeBag felszabadításakor.
Igen, a SwiftUI támogatja az ObservableObject-et @Published-del és @StateObject/@ObservedObject-del — ez egy beépített Observer implementáció. A @Published automatikusan értesíti a View-t a változásokról. A Combine nem kötelező: az ObservableObject a SwiftUI-ba épített objectWillChange Publisher-t használja. A Combine operátorokat ad hozzá a folyamok transzformálásához.
SharedFlow — egyszeri eseményekhez (navigáció, toast, Snackbar), ahol nem szükséges az aktuális érték. StateFlow — UI állapothoz (adatlista, betöltési állapot), ahol aktuális pillanatkép szükséges. A SharedFlow-nak nincs value tulajdonsága, és nem adja vissza az utolsó értéket az új előfizetőknek.
A KVO egy elavult ObjC mechanizmus, @objc dynamic-ot igényel és csak NSObject-ből származó osztályokkal működik. A Combine egy modern Swift keretrendszer, típusbiztos, operátorokkal és SwiftUI integrációval. A Combine helyettesíti a KVO-t és a NotificationCenter-t. Az Apple a Combine-t ajánlja új projektekhez, a KVO-t — csak a régi kód támogatásához.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is