NotificationCenter — egy iOS rendszermechanizmus az alkalmazáskomponensek közötti értesítések küldésére és fogadására a feladó és a vevő közötti közvetlen kapcsolat nélkül. Az Observer mintára épülve a NotificationCenter lehetővé teszi az objektumok számára, hogy feliratkozzanak eseményekre és aszinkron módon reagáljanak rájuk. Az Apple Documentation (2025) szerint az NSNotificationCenter támogatja az értesítések szinkron küldését a post(name:object:) segítségével, valamint a késleltetett küldést a NotificationQueue-n keresztül. Az értesítési központ egyetlen folyamaton belül működik, és nem lépi át az alkalmazások határait.
Főbb pontok
NotificationCenter (NSNotificationCenter) — egy beépített iOS mechanizmus a lazán kapcsolt kommunikáció megvalósításához objektumok között. Az Observer minta lehetővé teszi egy objektum (feladó) számára, hogy közvetlen hivatkozás nélkül értesítsen több más objektumot (megfigyelőt) egy esemény bekövetkeztéről. A NotificationCenter három entitással működik: Notification.Name (értesítés azonosítója), Notification (adatokat tartalmazó konténer) és NotificationCenter (diszpécser). Minden alkalmazás rendelkezik egy megosztott default center-rel.
Notification.Name — egy struktúra, amely azonosítja az értesítés típusát. Az extension Name: Notification.Name("MyNotification") segítségével hozható létre. A Notification — egy objektum, amely name, object (feladó) és userInfo (adatokat tartalmazó szótár) elemeket tartalmaz. A rendszerértesítések konstansként vannak deklarálva: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Az egyéni értesítéseket extension segítségével kell csoportosítani a névütközések elkerülése érdekében. A neveknek fordított-domainnek kell lenniük.
// Egyéni értesítések meghatározása
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Értesítés küldése adatokkal
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
A megfigyelő az addObserver(_:selector:name:object:) metódussal iratkozik fel egy értesítésre. Selector — a metódus, amely az értesítés vételekor kerül meghívásra. Az object paraméter lehetővé teszi az értesítések szűrését egy adott feladótól. Ha az object nil, a megfigyelő minden értesítést megkap a megadott névvel bármely feladótól. iOS 9-től kezdve az addObserver nem igényel kézi eltávolítást a block-based API esetében, de a selector-based még mindig removeObserver-t igényel.
// Feliratkozás értesítésre (selector-based)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleDataUpdate),
name: .dataDidUpdate,
object: nil
)
@objc func handleDataUpdate(_ notification: Notification) {
guard let userId = notification.userInfo?["userId"] as? Int else { return }
updateUI(for: userId)
}
// Feliratkozás értesítésre (block-based, iOS 9+)
var observer: NSObjectProtocol?
observer = NotificationCenter.default.addObserver(
forName: .dataDidUpdate,
object: nil,
queue: .main
) { [weak self] notification in
guard let self else { return }
self.handleNotification(notification)
}
A NotificationCenter egy leképezési táblát tárol (name → megfigyelők halmaza). Amikor a feladó meghívja a post(name:object:)-t, az értesítési központ szinkron módon bejárja az összes, erre a névre feliratkozott megfigyelőt, és meghívja azok szelektorait vagy blokkjait. Kulcsfontosságú jellemző: a post blokkolja az aktuális szálat az összes handler befejezéséig. Ha a handlerek nehéz műveleteket hajtanak végre, ez késlelteti a feladót. A NotificationQueue ezt a problémát oldja meg az értesítések kézbesítésének késleltetésével.
A post(name:object:userInfo:) metódus azonnal elküldi az értesítést az összes megfigyelőnek. A hívás szinkron — a post utáni kód csak az összes handler befejeződése után hajtódik végre. A megfigyelők hívási sorrendje nincs garantálva, és a futtatások között változhat. Szekvenciális feldolgozáshoz használja a NotificationQueue-t coalescing-gal. Ne hívja a post-ot ugyanazon értesítés handlerén belül — ez végtelen rekurzióhoz vezet.
NotificationQueue értesítéseket ad egy sorhoz aszinkron kézbesítés céljából. Támogatja a coalescing-ot (azonos értesítések összevonása) és a kézbesítési sor kiválasztását (asap, idle, modal). A coalescing hasznos gyakori eseményeknél (betöltési előrehaladás), amikor csak az utolsó értékkel kell értesíteni. A NotificationQueue a run loop-ot használja az aktiváláshoz, ezért csak aktív run loop-pal rendelkező szálakon működik.
// Késleltetett küldés NotificationQueue segítségével
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: több értesítés összevonása egybe
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Aszinkron küldés DispatchQueue segítségével
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
Az iOS három fő kommunikációs mechanizmust kínál objektumok között: NotificationCenter, Delegate és KVO (Key-Value Observing). Mindegyik megoldja az értesítési problémát, de különböző kompromisszumokkal a kapcsoltság, a teljesítmény és a típusbiztonság tekintetében. A mechanizmus kiválasztása az „egy-az-egyhez" vagy „egy-a-többhöz" kapcsolattól és az adatátviteli igénytől függ.
| Jellemző | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Kapcsoltság | Gyenge (értesítés neve) | Erős (protokoll) | Közepes (kulcs) |
| Kapcsolat | Egy-a-többhöz | Egy-az-egyhez | Egy-a-többhöz |
| Típusbiztonság | Alacsony (userInfo Dictionary-ként) | Magas (protokoll metódusai) | Közepes (Any?) |
| Teljesítmény | Közepes (tábla bejárása) | Magas (közvetlen hívás) | Alacsony (NSObject) |
| Aszinkronitás | Szinkron (post blokkol) | Szinkron a feladó szálában | Szinkron változáskor |
A NotificationCenter ideális olyan eseményekhez, amelyekre több független komponensnek kell reagálnia. Példák: alkalmazásbeállítások módosítása, felhasználó kijelentkezése, push értesítés fogadása a háttérben. A NotificationCenter alkalmas lazán kapcsolt modulokhoz is (A funkciónak nem kell tudnia B funkcióról). Hátrány — nincs típusbiztonság: a userInfo kulcsok sztringek, nem enumok.
Delegate-et válasszon egy-az-egyhez kapcsolathoz egyértelmű szerződéssel (tableView.delegate). A Delegate gyorsabb és típusbiztonságosabb. KVO-t válasszon egy modell egy adott tulajdonságának változásának megfigyeléséhez (isLoading, progress). A KVO az NSObject-től való öröklődést igényel, és nehézségeket okozhat a hibakeresésben (varázslatos kulcssztringek). A modern Swift-ben a Combine és az async sequences mindhárom megközelítést helyettesíti.
Az addObserver metódus két feliratkozási változatot támogat: selector-based (hagyományos) és block-based (lezárással). Selector-based @objc kompatibilitást és a megfigyelő kézi eltávolítását igényli. Block-based (iOS 9+) lehetővé teszi a capture lista használatát, és automatikusan kezeli az OS, ha erős referenciák nélküli blokkokat használ. A block-based támogatja a queue-t is — a megfigyelő a megadott sorban kapja az értesítést.
A feliratkozás hagyományos módja szelektor segítségével. A handler metódust @objc-vel kell megjelölni, és opcionális Notification-t kell fogadnia. Előny: bármely osztály használhatja, beleértve a legacy Objective-C-t is. Hátrányok: a szelektor típusbiztonságának hiánya, elírás kockázata a szelektor nevében, kötelező removeObserver a deinit-ben. Ha a megfigyelőt az objektum előtt távolítják el, a handler nem hívódik meg.
A block-based API egy lezárást fogad, amely az értesítés vételekor hajtódik végre. A queue paraméter határozza meg, hogy a blokk melyik sorban hajtódjon végre — main queue az UI-frissítésekhez vagy background queue az adatfeldolgozáshoz. A visszaadott NSObjectProtocol érték a megfigyelő eltávolítására szolgál: NotificationCenter.default.removeObserver(observer). A block-based előnyben részesítendő a modern Swift-ben.
protocol NotificationToken {
func dispose()
}
extension NotificationCenter {
func observe(
name: NSNotification.Name,
object: Any? = nil,
queue: OperationQueue? = .main,
using block: @escaping (Notification) -> Void
) -> NotificationToken {
let observer = addObserver(forName: name, object: object,
queue: queue, using: block)
return NotificationTokenWrapper(observer: observer, center: self)
}
}
// Használat automatikus eltávolítással
class ViewModel {
private var tokens: [NotificationToken] = []
func startObserving() {
let token = NotificationCenter.default.observe(
name: .dataDidUpdate,
queue: .main
) { [weak self] notification in
self?.handleUpdate(notification)
}
tokens.append(token)
}
deinit {
tokens.forEach { $0.dispose() }
}
}
Memóriaszivárgás — az egyik fő probléma a NotificationCenter-rel való munka során. Ha a megfigyelőt nem távolítják el a felszabadítás előtt, az értesítés küldésekor a központ megpróbálja meghívni a már felszabadított objektum metódusát, ami EXC_BAD_ACCESS-hez vezet. iOS 9-től kezdve a block-based addObserver gyenge referenciákat használ, de a selector-based még mindig kézi removeObserver-t igényel. Best practice: távolítsa el a megfigyelőt a deinit-ben.
Selector-based: kötelezően hívja meg a NotificationCenter.default.removeObserver(self)-t a deinit-ben. Ha a megfigyelő több értesítésre van feliratkozva, mindegyik egyszerre eltávolítható (paraméterek nélkül) vagy egy adott név alapján. Block-based: távolítsa el a removeObserver segítségével az addObserver-től kapott token segítségével. A block-based esetében iOS 9+ rendszeren nincs szivárgás, de az eltávolítás továbbra is ajánlott a teljesítmény érdekében: a felszabadított megfigyelők nem lesznek bejárva a post során.
class SafeObserver {
private var observers: [NSObjectProtocol] = []
func addSubscriptions() {
let token1 = NotificationCenter.default.addObserver(
forName: .dataDidUpdate, object: nil,
queue: .main) { [weak self] _ in
self?.refreshData()
}
let token2 = NotificationCenter.default.addObserver(
forName: .userLoggedOut, object: nil,
queue: .main) { [weak self] _ in
self?.logout()
}
observers.append(contentsOf: [token1, token2])
}
deinit {
observers.forEach { NotificationCenter.default.removeObserver($0) }
}
private func refreshData() { }
private func logout() { }
}
A Token minta automatizálja a megfigyelők kezelését. Feliratkozáskor egy token-objektum (NSObjectProtocol) kerül visszaadásra, amely a felszabadításkor automatikusan eltávolítja a megfigyelőt. NotificationTokenWrapper gyenge referenciát tárol a NotificationCenter-re és a megfigyelő tokenre, meghívva a removeObserver-t a deinit-ben. Ez közelebb hozza a NotificationCenter-t a Combine megközelítéshez, ahol az AnyCancellable kezeli a feliratkozás életciklusát.
Thread safety A NotificationCenter garantálja, hogy a post bármely szálból meghívható, és minden megfigyelő ugyanabban a szálban kapja az értesítést, ahol a post-ot meghívták. Ez kritikus a több szálas alkalmazásoknál: ha az értesítést egy háttérszálból küldték, a handlerek is a háttérszálban hajtódnak végre. Az UI frissítéséhez a feldolgozást a main queue-ba kell diszponálni a DispatchQueue.main.async segítségével.
A NotificationCenter szálbiztos a post és addObserver hívásokhoz különböző szálakból. Belső szinkronizáció zárolást használ, ezért a gyakori post több szálból contention-t okozhat. Nagy terhelésű forgatókönyvekhez (1000 fájl betöltési állapota) használjon külön értesítési sort vagy Combine publisher-t. A NotificationQueue postingStyle .now értékkel egyenértékű a közvetlen post-tal.
A NotificationCenter támogatja a Combine publisher-t a NotificationCenter.default.publisher(for:object:) segítségével. Publisher minden értesítést Combine eseménnyé alakít, amely map, filter, debounce és throttle segítségével transzformálható. Ez megoldja a szinkron kézbesítés problémáját: a Combine aszinkron módon dolgozza fel az értesítéseket a megadott Scheduler-en. A NotificationCenter.publisher — híd a legacy mechanizmus és a modern reaktív programozás között.
import Combine
class ReactiveViewModel {
private var cancellables = Set<AnyCancellable>()
func setupCombineSubscription() {
NotificationCenter.default
.publisher(for: .dataDidUpdate)
.receive(on: DispatchQueue.main)
.compactMap { $0.userInfo?["progress"] as? Float }
.debounce(for: .seconds(0.3), scheduler: RunLoop.main)
.sink { [weak self] progress in
self?.progressLabel.text = "\(Int(progress * 100))%"
}
.store(in: &cancellables)
}
}
Gyakran ismételt kérdések
Igen, a NotificationCenter szálbiztos a post és addObserver hívásokhoz bármely szálból. Azonban a handlerek ugyanabban a szálban hajtódnak végre, ahol a post-ot meghívták. Az UI frissítéséhez használja a queue: .main-t a block-based addObserver-ben vagy a DispatchQueue.main.async-t a handleren belül. A Combine publisher a receive(on:)-nel szintén megoldja a szálproblémát.
Selector-based: crash EXC_BAD_ACCESS az értesítés küldésekor a megfigyelő felszabadítása után. Block-based (iOS 9+): nincs szivárgás a gyenge referenciának köszönhetően, de az értesítési központ továbbra is memóriában tartja a blokkot az explicit removeObserver-ig. Ajánlott mindig eltávolítani a megfigyelőt a deinit-ben, vagy a Token mintát használni az automatikus kezeléshez.
NotificationCenter — tetszőleges események szórása nem kapcsolódó komponensek között. KVO — egy adott objektum adott tulajdonságának változásának megfigyelése. A KVO az NSObject-től való öröklődést igényel, és automatikusan értesít a tulajdonság setteren keresztüli változásakor. A NotificationCenter csak explicit post híváskor értesít. Modell megfigyeléséhez a KVO vagy a Combine előnyösebb.
Egy default center alkalmazásfolyamatonként. További központok hozhatók létre a NotificationCenter() segítségével, de a gyakorlatban a megosztott default-ot használják. Minden központ függetlenül működik — az egyikben történő post nem kerül kézbesítésre a másik megfigyelőinek. A modulok elkülönítéséhez használjon külön Name névtereket fordított-domain értesítési nevekkel.
Részben. A Combine NotificationCenter.Publisher-t biztosít, amely a NotificationCenter-t egy reaktív folyamba csomagolja. A Combine megoldja a szinkronitás problémáját (a receive(on:) segítségével), transzformációs operátorokat és automatikus feliratkozás-kezelést (AnyCancellable) ad hozzá. Azonban a NotificationCenter továbbra is megmarad az iOS rendszerértesítéseihez (UIApplication, UIKeyboard) és a legacy kódhoz. A Combine egy kiegészítő réteg, nem helyettesítés.
Összefoglalá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