NotificationCenter — je systémový mechanismus iOS pro odesílání a přijímání oznámení mezi komponentami aplikace bez přímého propojení mezi odesílatelem a příjemcem. Založený na vzoru Observer umožňuje NotificationCenter objektům přihlásit se k odběru událostí a reagovat na ně asynchronně. Podle Apple Documentation (2025) podporuje NSNotificationCenter jak synchronní odesílání oznámení pomocí post(name:object:), tak odložené odesílání pomocí NotificationQueue. Centrum oznámení pracuje v rámci jednoho procesu a nepřekračuje hranice aplikací.
Hlavní body
NotificationCenter (NSNotificationCenter) — je vestavěný mechanismus iOS pro implementaci volně vázané komunikace mezi objekty. Vzor Observer umožňuje jednomu objektu (odesílateli) informovat mnoho dalších objektů (pozorovatelů) o výskytu události bez přímého odkazu na ně. NotificationCenter pracuje se třemi entitami: Notification.Name (identifikátor oznámení), Notification (kontejner s daty) a NotificationCenter (dispečer). Každá aplikace má sdílené default center.
Notification.Name — je struktura identifikující typ oznámení. Vytváří se pomocí extension Name: Notification.Name("MyNotification"). Notification — je objekt obsahující name, object (odesílatele) a userInfo (slovník s daty). Systémová oznámení jsou deklarována jako konstanty: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Vlastní oznámení by měla být seskupena pomocí extension, aby se předešlo kolizím názvů. Názvy by měly být reverzní-domény.
// Definování vlastních oznámení
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Odeslání oznámení s daty
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
Pozorovatel se přihlašuje k oznámení pomocí metody addObserver(_:selector:name:object:). Selector — metoda, která bude volána při přijetí oznámení. Parametr object umožňuje filtrovat oznámení od konkrétního odesílatele. Pokud je object nil, pozorovatel přijímá všechna oznámení se zadaným názvem od libovolných odesílatelů. Od iOS 9 addObserver nevyžaduje ruční odstranění pro block-based API, ale selector-based stále vyžaduje removeObserver.
// Přihlášení k oznámení (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)
}
// Přihlášení k oznámení (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)
}
NotificationCenter ukládá mapovací tabulku (name → množina pozorovatelů). Když odesílatel zavolá post(name:object:), centrum oznámení synchronně prochází všechny pozorovatele přihlášené k tomuto názvu a volá jejich selektory nebo bloky. Klíčová vlastnost: post blokuje aktuální vlákno až do dokončení všech handlerů. Pokud handlery provádějí náročné operace, zdržuje to odesílatele. NotificationQueue řeší tento problém zpožděním doručení oznámení.
Metoda post(name:object:userInfo:) odesílá oznámení okamžitě všem pozorovatelům. Volání je synchronní — kód po post se provádí až po dokončení všech handlerů. Pořadí volání pozorovatelů není zaručeno a může se mezi spuštěními měnit. Pro sekvenční zpracování použijte NotificationQueue s coalescingem. Nevolejte post uvnitř handleru stejného oznámení — to vede k nekonečné rekurzi.
NotificationQueue přidává oznámení do fronty pro asynchronní doručení. Podporuje coalescing (slučování identických oznámení) a výběr fronty doručení (asap, idle, modal). Coalescing je užitečný pro časté události (průběh načítání), kdy je třeba informovat pouze poslední hodnotou. NotificationQueue používá run loop pro aktivaci, takže funguje pouze ve vláknech s aktivním run loopem.
// Odložené odesílání přes NotificationQueue
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: několik oznámení se sloučí do jednoho
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Asynchronní odesílání přes DispatchQueue
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
iOS poskytuje tři hlavní mechanismy komunikace mezi objekty: NotificationCenter, Delegate a KVO (Key-Value Observing). Každý řeší problém oznamování, ale s různými kompromisy v oblasti provázanosti, výkonu a typové bezpečnosti. Výběr mechanismu závisí na vztahu „jeden-k-jednomu" nebo „jeden-k-mnoha" a potřebě přenosu dat.
| Vlastnost | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Provázanost | Volná (název oznámení) | Silná (protokol) | Střední (klíč) |
| Vztah | Jeden-k-mnoha | Jeden-k-jednomu | Jeden-k-mnoha |
| Typová bezpečnost | Nízká (userInfo jako Dictionary) | Vysoká (metody protokolu) | Střední (Any?) |
| Výkon | Střední (průchod tabulkou) | Vysoký (přímé volání) | Nízký (NSObject) |
| Asynchronita | Synchronně (post blokuje) | Synchronně ve vlákně odesílatele | Synchronně při změně |
NotificationCenter je ideální pro události, na které musí reagovat několik nezávislých komponent. Příklady: změna nastavení aplikace, odhlášení uživatele, přijetí push oznámení na pozadí. NotificationCenter je také vhodný pro volně propojené moduly (funkce A nemusí vědět o funkci B). Nevýhoda — žádná typová bezpečnost: klíče userInfo jsou řetězce, nikoli enum.
Delegate zvolte pro vztah jeden-k-jednomu s jasným kontraktem (tableView.delegate). Delegate je rychlejší a typově bezpečnější. KVO zvolte pro sledování změny konkrétní vlastnosti modelu (isLoading, progress). KVO vyžaduje dědění z NSObject a může způsobit potíže při ladění (magické řetězce klíčů). V moderním Swiftu Combine a async sequences nahrazují všechny tři přístupy.
Metoda addObserver podporuje dvě varianty přihlášení: selector-based (tradiční) a block-based (s uzávěrem). Selector-based vyžaduje kompatibilitu s @objc a ruční odstranění pozorovatele. Block-based (iOS 9+) umožňuje použití capture list a je automaticky spravován OS při použití bloků bez silných referencí. Block-based také podporuje queue — pozorovatel přijímá oznámení v určené frontě.
Tradiční způsob přihlášení pomocí selektoru. Metoda handleru musí být označena @objc a přijímat volitelný Notification. Výhoda: možnost použití libovolnou třídou, včetně legacy Objective-C. Nevýhody: chybějící typová bezpečnost selektoru, riziko překlepů v názvu selektoru, povinný removeObserver v deinit. Pokud je pozorovatel odstraněn dříve než objekt, handler nebude volán.
Block-based API přijímá uzávěr, který se provede při přijetí oznámení. Parametr queue určuje, ve které frontě se blok provede — main queue pro aktualizace UI nebo background queue pro zpracování dat. Návratová hodnota NSObjectProtocol se používá k odstranění pozorovatele: NotificationCenter.default.removeObserver(observer). Block-based je preferován v moderním Swiftu.
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)
}
}
// Použití s automatickým odstraněním
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() }
}
}
Úniky paměti — jeden z hlavních problémů při práci s NotificationCenter. Pokud pozorovatel není odstraněn před dealokací, při odesílání oznámení se centrum pokusí zavolat metodu již uvolněného objektu, což vede k EXC_BAD_ACCESS. Od iOS 9 používá block-based addObserver slabé reference, ale selector-based stále vyžaduje ruční removeObserver. Best practice: odstraňte pozorovatele v deinit.
Selector-based: povinně volejte NotificationCenter.default.removeObserver(self) v deinit. Pokud je pozorovatel přihlášen k několika oznámením, lze odstranit všechna najednou (bez parametrů) nebo konkrétní podle názvu. Block-based: odstraňte pomocí removeObserver s tokenem získaným z addObserver. Pro block-based na iOS 9+ nedochází k úniku, ale odstranění je stále doporučeno pro výkon: uvolnění pozorovatelé nebudou při post procházeni.
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() { }
}
Vzor Token automatizuje správu pozorovatelů. Při přihlášení je vrácen objekt-token (NSObjectProtocol), který při dealokaci automaticky odstraní pozorovatele. NotificationTokenWrapper uchovává slabou referenci na NotificationCenter a token pozorovatele a volá removeObserver v deinit. Tím se NotificationCenter přibližuje přístupu Combine, kde AnyCancellable spravuje životní cyklus přihlášení.
Thread safety NotificationCenter zaručuje, že post může být volán z libovolného vlákna a všichni pozorovatelé obdrží oznámení ve stejném vlákně, kde byl post volán. To je kritické pro vícevláknové aplikace: pokud bylo oznámení odesláno z vlákna na pozadí, handlery se také provedou ve vlákně na pozadí. Pro aktualizaci UI je třeba zpracování dispatchnout na main queue pomocí DispatchQueue.main.async.
NotificationCenter je bezpečný pro vlákna pro volání post a addObserver z různých vláken. Interní synchronizace používá zamykání, takže časté post z více vláken může vytvářet contention. Pro scénáře s vysokým zatížením (průběh načítání 1000 souborů) použijte samostatnou frontu oznámení nebo Combine publisher. NotificationQueue s postingStyle .now je ekvivalentní přímému post.
NotificationCenter podporuje Combine publisher prostřednictvím NotificationCenter.default.publisher(for:object:). Publisher převádí každé oznámení na událost Combine, kterou lze transformovat pomocí map, filter, debounce a throttle. To řeší problém synchronního doručení: Combine zpracovává oznámení asynchronně na určeném Scheduleru. NotificationCenter.publisher — most mezi legacy mechanismem a moderním reaktivním programováním.
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)
}
}
Často kladené otázky
Ano, NotificationCenter je bezpečný pro vlákna pro volání post a addObserver z libovolných vláken. Handlery se však provádějí ve stejném vlákně, kde byl volán post. Pro aktualizaci UI použijte queue: .main v block-based addObserver nebo DispatchQueue.main.async uvnitř handleru. Combine publisher s receive(on:) také řeší problém vlákna.
Selector-based: crash EXC_BAD_ACCESS při odesílání oznámení po dealokaci pozorovatele. Block-based (iOS 9+): nedochází k úniku díky slabé referenci, ale centrum oznámení nadále uchovává blok v paměti až do explicitního removeObserver. Doporučuje se vždy odstranit pozorovatele v deinit nebo použít vzor Token pro automatickou správu.
NotificationCenter — vysílání libovolných událostí mezi nesouvisejícími komponentami. KVO — sledování změny konkrétní vlastnosti konkrétního objektu. KVO vyžaduje dědění z NSObject a automaticky informuje při změně vlastnosti pomocí setteru. NotificationCenter informuje pouze při explicitním volání post. Pro sledování modelu je preferováno KVO nebo Combine.
Jedno default center na proces aplikace. Další centra lze vytvořit pomocí NotificationCenter(), ale v praxi se používá sdílené default. Každé centrum funguje nezávisle — post v jednom není doručován pozorovatelům druhého. Pro izolaci modulů používejte oddělené jmenné prostory Name pomocí reverzních-doménových názvů oznámení.
Částečně. Combine poskytuje NotificationCenter.Publisher, který obaluje NotificationCenter do reaktivního streamu. Combine řeší problém synchronnosti (pomocí receive(on:)), přidává transformační operátory a automatickou správu přihlášení (AnyCancellable). Nicméně NotificationCenter zůstává pro systémová oznámení iOS (UIApplication, UIKeyboard) a legacy kód. Combine je nadstavba, nikoli náhrada.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také