NotificationCenter — is een systeemmechanisme in iOS voor het verzenden en ontvangen van meldingen tussen applicatiecomponenten zonder directe verbinding tussen verzender en ontvanger. Gebaseerd op het Observer-patroon, stelt NotificationCenter objecten in staat zich te abonneren op gebeurtenissen en er asynchroon op te reageren. Volgens Apple Documentation (2025) ondersteunt NSNotificationCenter zowel synchrone verzending via post(name:object:) als uitgestelde verzending via NotificationQueue. Het meldingscentrum werkt binnen één proces en overschrijdt geen applicatiegrenzen.
Belangrijkste punten
NotificationCenter (NSNotificationCenter) — is een ingebouwd iOS-mechanisme voor het implementeren van losjes gekoppelde communicatie tussen objecten. Het Observer-patroon stelt één object (verzender) in staat meerdere andere objecten (waarnemers) op de hoogte te stellen van een gebeurtenis zonder directe verwijzing ernaar. NotificationCenter werkt met drie entiteiten: Notification.Name (meldingsidentificatie), Notification (container met gegevens) en NotificationCenter (dispatcher). Elke applicatie heeft een gedeeld default center.
Notification.Name — is een structuur die het meldingstype identificeert. Wordt gemaakt via extension Name: Notification.Name("MyNotification"). Notification — is een object dat name, object (verzender) en userInfo (woordenboek met gegevens) bevat. Systeemmeldingen zijn gedeclareerd als constanten: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Aangepaste meldingen moeten via extension worden gegroepeerd om naamconflicten te voorkomen. Namen moeten omgekeerd-domein zijn.
// Definiëren van aangepaste meldingen
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Melding met gegevens verzenden
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
De waarnemer abonneert zich op een melding via de methode addObserver(_:selector:name:object:). Selector — de methode die wordt aangeroepen bij ontvangst van de melding. De parameter object maakt het mogelijk meldingen van een specifieke verzender te filteren. Als object nil is, ontvangt de waarnemer alle meldingen met de opgegeven naam van elke verzender. Vanaf iOS 9 vereist addObserver geen handmatige verwijdering voor block-based API, maar selector-based vereist nog steeds removeObserver.
// Abonneren op melding (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)
}
// Abonneren op melding (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 slaat een mappingtabel op (name → set waarnemers). Wanneer de verzender post(name:object:) aanroept, doorloopt het meldingscentrum synchroon alle waarnemers die op deze naam zijn geabonneerd en roept hun selectors of blokken aan. Belangrijk kenmerk: post blokkeert de huidige thread totdat alle handlers zijn voltooid. Als handlers zware bewerkingen uitvoeren, vertraagt dit de verzender. NotificationQueue lost dit probleem op door de meldingslevering uit te stellen.
De methode post(name:object:userInfo:) verzendt de melding onmiddellijk naar alle waarnemers. De aanroep is synchroon — code na post wordt pas uitgevoerd nadat alle handlers zijn voltooid. De aanroepvolgorde van waarnemers is niet gegarandeerd en kan variëren tussen uitvoeringen. Gebruik voor sequentiële verwerking NotificationQueue met coalescing. Roep post niet aan binnen een handler van dezelfde melding — dit leidt tot oneindige recursie.
NotificationQueue voegt meldingen toe aan een wachtrij voor asynchrone levering. Ondersteunt coalescing (samenvoegen van identieke meldingen) en selectie van de leveringswachtrij (asap, idle, modal). Coalescing is handig voor frequente gebeurtenissen (laadvoortgang), wanneer alleen met de laatste waarde hoeft te worden gemeld. NotificationQueue gebruikt run loop om te activeren, dus werkt alleen in threads met een actieve run loop.
// Uitgestelde verzending via NotificationQueue
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: meerdere meldingen worden samengevoegd
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Asynchrone verzending via DispatchQueue
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
iOS biedt drie hoofdmechanismen voor communicatie tussen objecten: NotificationCenter, Delegate en KVO (Key-Value Observing). Elk lost het meldingsprobleem op, maar met verschillende afwegingen op het gebied van koppeling, prestaties en typeveiligheid. De keuze van het mechanisme hangt af van de relatie 'een-op-een' of 'een-op-veel' en de noodzaak om gegevens over te dragen.
| Kenmerk | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Koppeling | Los (meldingsnaam) | Sterk (protocol) | Gemiddeld (sleutel) |
| Relatie | Een-op-veel | Een-op-een | Een-op-veel |
| Typeveiligheid | Laag (userInfo als Dictionary) | Hoog (protocolmethoden) | Gemiddeld (Any?) |
| Prestaties | Gemiddeld (tabel doorlopen) | Hoog (directe aanroep) | Laag (NSObject) |
| Asynchroniteit | Synchroon (post blokkeert) | Synchroon in thread verzender | Synchroon bij wijziging |
NotificationCenter is ideaal voor gebeurtenissen waarop meerdere onafhankelijke componenten moeten reageren. Voorbeelden: wijziging van app-instellingen, uitloggen van gebruiker, ontvangen van een pushmelding op de achtergrond. NotificationCenter is ook geschikt voor losjes gekoppelde modules (functie A hoeft niet van functie B te weten). Nadeel — geen typeveiligheid: userInfo-sleutels zijn strings, geen enum.
Delegate kiest u voor een een-op-een relatie met een duidelijk contract (tableView.delegate). Delegate is sneller en typeveiliger. KVO kiest u voor het observeren van een specifieke eigenschap van een model (isLoading, progress). KVO vereist overerving van NSObject en kan problemen veroorzaken bij het debuggen (magische sleutelstrings). In modern Swift vervangen Combine en async sequences alle drie de benaderingen.
De methode addObserver ondersteunt twee abonnementsvarianten: selector-based (traditioneel) en block-based (met closure). Selector-based vereist @objc-compatibiliteit en handmatige verwijdering van de waarnemer. Block-based (iOS 9+) maakt gebruik van capture list mogelijk en wordt automatisch beheerd door het OS bij gebruik van blokken zonder sterke referenties. Block-based ondersteunt ook queue — de waarnemer ontvangt de melding in de opgegeven wachtrij.
Traditionele manier van abonneren via selector. De handlermethode moet zijn gemarkeerd met @objc en een optionele Notification accepteren. Voordeel: bruikbaar door elke klasse, inclusief legacy Objective-C. Nadelen: geen typeveiligheid van de selector, risico op typefouten in de selectornaam, verplichte removeObserver in deinit. Als de waarnemer vóór het object wordt verwijderd, wordt de handler niet aangeroepen.
Block-based API accepteert een closure die wordt uitgevoerd bij ontvangst van de melding. Parameter queue bepaalt in welke wachtrij het blok wordt uitgevoerd — main queue voor UI-updates of background queue voor gegevensverwerking. De geretourneerde NSObjectProtocol-waarde wordt gebruikt om de waarnemer te verwijderen: NotificationCenter.default.removeObserver(observer). Block-based heeft de voorkeur in modern 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)
}
}
// Gebruik met automatische verwijdering
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() }
}
}
Geheugenlekken — een van de belangrijkste problemen bij het werken met NotificationCenter. Als de waarnemer niet wordt verwijderd vóór deallocatie, zal het centrum bij het verzenden van de melding proberen de methode van het reeds vrijgegeven object aan te roepen, wat leidt tot EXC_BAD_ACCESS. Vanaf iOS 9 gebruikt block-based addObserver zwakke referenties, maar selector-based vereist nog steeds handmatige removeObserver. Best practice: verwijder de waarnemer in deinit.
Selector-based: roep verplicht NotificationCenter.default.removeObserver(self) aan in deinit. Als de waarnemer op meerdere meldingen is geabonneerd, kunnen ze allemaal tegelijk (zonder parameters) of een specifieke op naam worden verwijderd. Block-based: verwijder via removeObserver met de token verkregen van addObserver. Voor block-based op iOS 9+ treedt geen lek op, maar verwijdering wordt nog steeds aanbevolen voor prestaties: vrijgegeven waarnemers worden niet doorlopen bij post.
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() { }
}
Het Token-patroon automatiseert het beheer van waarnemers. Bij abonnering wordt een token-object (NSObjectProtocol) geretourneerd, dat bij deallocatie automatisch de waarnemer verwijdert. NotificationTokenWrapper slaat een zwakke referentie naar NotificationCenter en de waarnemer-token op, en roept removeObserver aan in deinit. Dit brengt NotificationCenter dichter bij de Combine-benadering, waar AnyCancellable de levenscyclus van het abonnement beheert.
Thread safety NotificationCenter garandeert dat post vanuit elke thread kan worden aangeroepen en alle waarnemers de melding ontvangen in dezelfde thread waar post is aangeroepen. Dit is cruciaal voor multi-thread applicaties: als de melding vanuit een achtergrondthread is verzonden, worden handlers ook in de achtergrondthread uitgevoerd. Voor UI-updates moet de verwerking naar de main queue worden gedispatched via DispatchQueue.main.async.
NotificationCenter is threadveilig voor aanroepen van post en addObserver vanuit verschillende threads. Interne synchronisatie gebruikt een vergrendeling, dus frequente post vanuit meerdere threads kan contention veroorzaken. Gebruik voor scenario's met hoge belasting (laadvoortgang van 1000 bestanden) een aparte meldingswachtrij of Combine publisher. NotificationQueue met postingStyle .now is gelijk aan directe post.
NotificationCenter ondersteunt Combine publisher via NotificationCenter.default.publisher(for:object:). Publisher verandert elke melding in een Combine-gebeurtenis die kan worden getransformeerd via map, filter, debounce en throttle. Dit lost het probleem van synchrone levering op: Combine verwerkt meldingen asynchroon op een opgegeven Scheduler. NotificationCenter.publisher — is een brug tussen het legacy-mechanisme en modern reactief programmeren.
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)
}
}
Veelgestelde vragen
Ja, NotificationCenter is threadveilig voor aanroepen van post en addObserver vanuit elke thread. Handlers worden echter uitgevoerd in dezelfde thread waar post is aangeroepen. Gebruik voor UI-updates queue: .main in block-based addObserver of DispatchQueue.main.async binnen de handler. Combine publisher met receive(on:) lost ook het threadprobleem op.
Selector-based: crash EXC_BAD_ACCESS bij het verzenden van de melding na deallocatie van de waarnemer. Block-based (iOS 9+): geen lek dankzij zwakke referentie, maar het meldingscentrum blijft het blok in het geheugen houden tot expliciete removeObserver. Het wordt aanbevolen de waarnemer altijd in deinit te verwijderen of het Token-patroon te gebruiken voor automatisch beheer.
NotificationCenter — uitzending van willekeurige gebeurtenissen tussen niet-gerelateerde componenten. KVO — observatie van een specifieke eigenschap van een specifiek object. KVO vereist overerving van NSObject en meldt automatisch bij wijziging van de eigenschap via setter. NotificationCenter meldt alleen bij expliciete aanroep van post. Voor modelobservatie hebben KVO of Combine de voorkeur.
Eén default center per applicatieproces. Extra centra kunnen worden gemaakt via NotificationCenter(), maar in de praktijk wordt de gedeelde default gebruikt. Elk centrum werkt onafhankelijk — post in het ene wordt niet bezorgd aan waarnemers van het andere. Gebruik voor module-isolatie aparte Name-naamruimten via omgekeerd-domein meldingsnamen.
Gedeeltelijk. Combine biedt NotificationCenter.Publisher die NotificationCenter in een reactieve stroom verpakt. Combine lost het synchronisatieprobleem op (via receive(on:)), voegt transformatieoperators en automatisch abonnementsbeheer (AnyCancellable) toe. NotificationCenter blijft echter voor systeemmeldingen van iOS (UIApplication, UIKeyboard) en legacy-code. Combine is een toevoeging, geen vervanging.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook