NotificationCenter — podstata, princip fungování a architektura oznámení

Autor: IT Sectr Publikováno: 2026-03-18 Doba čtení: 10 min

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 — implementace vzoru Observer pro výměnu událostí mezi komponentami iOS.
  • addObserver přihlašuje objekt k oznámením s určitým názvem a objektem odesílatele.
  • post(name:object:) odesílá oznámení všem přihlášeným pozorovatelům synchronně.
  • removeObserver je povinný v deinit, jinak dojde k pádu při odesílání oznámení.
  • NotificationQueue umožňuje odkládat oznámení pro asynchronní doručení.

Co je NotificationCenter?

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.

NSNotification a Notification.Name

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.

swift
// 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
)

Přidání pozorovatele (addObserver)

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.

swift
// 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)
}

Jak funguje NotificationCenter?

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í.

Synchronní odesílání (post)

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.

Odložené odesílání (NotificationQueue)

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.

swift
// 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)
}

Notification vs Delegate vs KVO

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.

VlastnostNotificationCenterDelegateKVO
ProvázanostVolná (název oznámení)Silná (protokol)Střední (klíč)
VztahJeden-k-mnohaJeden-k-jednomuJeden-k-mnoha
Typová bezpečnostNízká (userInfo jako Dictionary)Vysoká (metody protokolu)Střední (Any?)
VýkonStřední (průchod tabulkou)Vysoký (přímé volání)Nízký (NSObject)
AsynchronitaSynchronně (post blokuje)Synchronně ve vlákně odesílateleSynchronně při změně

Kdy zvolit NotificationCenter

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.

Kdy zvolit Delegate nebo KVO

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.

AddObserver: synchronní a asynchronní oznámení

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ě.

Selector-based addObserver

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 addObserver

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.

swift
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() }
    }
}

Správa paměti a odstraňování pozorovatelů

Ú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.

Kdy volat removeObserver

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.

swift
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() { }
}

Slabé reference pomocí vzoru Token

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í.

NotificationCenter ve vícevláknovém prostředí

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.

Bezpečnost vláken pro post a addObserver

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.

Asynchronní doručení pomocí Combine

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.

swift
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

Je NotificationCenter bezpečný pro vlákna?

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.

Co se stane, když pozorovatel není odstraněn?

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.

Jaký je rozdíl mezi NotificationCenter a KVO?

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.

Kolik NotificationCenter existuje v aplikaci?

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í.

Nahrazuje Combine NotificationCenter?

Čá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í

  • NotificationCenter — implementace vzoru Observer pro volně vázanou komunikaci „jeden-k-mnoha" v iOS.
  • post odesílá oznámení synchronně všem pozorovatelům v aktuálním vlákně, blokuje odesílatele.
  • addObserver podporuje selector-based (s @objc) a block-based (s capture list a queue) přihlášení.
  • removeObserver je povinný v deinit pro selector-based přihlášení, jinak crash.
  • NotificationQueue poskytuje odložené doručení s coalescingem pro časté události.
  • Thread safety zaručuje práci z libovolného vlákna, ale handlery se provádějí ve vlákně odesílatele.
  • Použijte vzor Token nebo Combine publisher pro bezpečnou a moderní správu přihlášení.

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í.

Prodiskutovat projekt

Přečtěte si také