NotificationCenter ist ein Systemmechanismus in iOS zum Senden und Empfangen von Benachrichtigungen zwischen Anwendungskomponenten ohne direkte Verbindung zwischen Sender und Empfänger. Basierend auf dem Observer-Muster ermöglicht NotificationCenter Objekten, Ereignisse zu abonnieren und asynchron darauf zu reagieren. Laut Apple Documentation (2025) unterstützt NSNotificationCenter sowohl das synchrone Senden von Benachrichtigungen über post(name:object:) als auch das verzögerte Senden über NotificationQueue. Das Benachrichtigungscenter arbeitet innerhalb eines einzelnen Prozesses und überschreitet keine Anwendungsgrenzen.
Wichtige Punkte
NotificationCenter (NSNotificationCenter) ist ein integrierter iOS-Mechanismus zur Implementierung von lose gekoppelter Kommunikation zwischen Objekten. Das Observer-Muster ermöglicht es einem Objekt (Sender), mehrere andere Objekte (Beobachter) über ein Ereignis zu benachrichtigen, ohne einen direkten Verweis auf sie zu haben. NotificationCenter arbeitet mit drei Entitäten: Notification.Name (Benachrichtigungskennung), Notification (Container mit Daten) und NotificationCenter (Dispatcher). Jede Anwendung hat ein gemeinsames default center.
Notification.Name ist eine Struktur, die den Benachrichtigungstyp identifiziert. Erstellt über extension Name: Notification.Name(“MyNotification”). Notification ist ein Objekt, das name, object (Sender) und userInfo (Wörterbuch mit Daten) enthält. Systembenachrichtigungen werden als Konstanten deklariert: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Benutzerdefinierte Benachrichtigungen sollten über extension gruppiert werden, um Namenskollisionen zu vermeiden. Namen sollten Reverse-Domain sein.
// Definieren von benutzerdefinierten Benachrichtigungen
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Senden einer Benachrichtigung mit Daten
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
Ein Beobachter abonniert eine Benachrichtigung über die Methode addObserver(_:selector:name:object:). Selector ist die Methode, die beim Empfang der Benachrichtigung aufgerufen wird. Der Parameter object ermöglicht das Filtern von Benachrichtigungen eines bestimmten Senders. Wenn object nil ist, empfängt der Beobachter alle Benachrichtigungen mit dem angegebenen Namen von jedem Sender. Seit iOS 9 erfordert addObserver für die block-based API keine manuelle Entfernung, aber selector-based erfordert weiterhin removeObserver.
// Abonnieren einer Benachrichtigung (selector-basiert)
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)
}
// Abonnieren einer Benachrichtigung (block-basiert, 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 speichert eine Zuordnungstabelle (Name → Menge der Beobachter). Wenn der Sender post(name:object:) aufruft, durchläuft das Benachrichtigungscenter synchron alle für diesen Namen abonnierten Beobachter und ruft deren Selectoren oder Blöcke auf. Wichtiges Merkmal: post blockiert den aktuellen Thread, bis alle Handler abgeschlossen sind. Wenn Handler schwere Operationen ausführen, verzögert dies den Sender. NotificationQueue löst dieses Problem, indem es die Zustellung von Benachrichtigungen verzögert.
Die Methode post(name:object:userInfo:) sendet eine Benachrichtigung sofort an alle Beobachter. Der Aufruf ist synchron — Code nach post wird erst ausgeführt, nachdem alle Handler abgeschlossen sind. Die Reihenfolge des Aufrufs der Beobachter ist nicht garantiert und kann zwischen Starts variieren. Für die sequenzielle Verarbeitung verwenden Sie NotificationQueue mit Coalescing. Rufen Sie post nicht innerhalb eines Handlers für dieselbe Benachrichtigung auf — dies führt zu unendlicher Rekursion.
NotificationQueue fügt Benachrichtigungen zur asynchronen Zustellung in eine Warteschlange ein. Es unterstützt Coalescing (Zusammenführen identischer Benachrichtigungen) und die Auswahl der Zustellungswarteschlange (asap, idle, modal). Coalescing ist nützlich für häufige Ereignisse (Download-Fortschritt), wenn Sie nur mit dem letzten Wert benachrichtigen müssen. NotificationQueue verwendet die Run Loop zum Auslösen, funktioniert daher nur in Threads mit aktiver Run Loop.
// Verzögerte Zustellung über NotificationQueue
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: mehrere Benachrichtigungen werden zu einer zusammengeführt
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Asynchrone Zustellung über DispatchQueue
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
iOS bietet drei Hauptmechanismen für die Kommunikation zwischen Objekten: NotificationCenter, Delegate und KVO (Key-Value Observing). Jeder löst das Benachrichtigungsproblem, jedoch mit unterschiedlichen Kompromissen bei Kopplung, Leistung und Typsicherheit. Die Wahl des Mechanismus hängt von der Eins-zu-Eins- oder Eins-zu-Viele-Beziehung und der Notwendigkeit der Datenübertragung ab.
| Eigenschaft | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Kopplung | Schwach (Benachrichtigungsname) | Stark (Protokoll) | Mittel (Schlüssel) |
| Beziehung | Eins-zu-Viele | Eins-zu-Eins | Eins-zu-Viele |
| Typsicherheit | Niedrig (userInfo als Dictionary) | Hoch (Protokollmethoden) | Mittel (Any?) |
| Leistung | Mittel (Tabellendurchlauf) | Hoch (direkter Aufruf) | Niedrig (NSObject) |
| Asynchronität | Synchron (post blockiert) | Synchron im Thread des Senders | Synchron bei Änderung |
NotificationCenter ist ideal für Ereignisse, auf die mehrere unabhängige Komponenten reagieren müssen. Beispiele: Änderungen der Anwendungseinstellungen, Benutzerabmeldung, Empfangen einer Push-Benachrichtigung im Hintergrund. NotificationCenter eignet sich auch für lose gekoppelte Module (Feature A sollte nichts von Feature B wissen). Der Nachteil ist der Mangel an Typsicherheit: userInfo-Schlüssel sind Strings, keine Enums.
Delegate ist die Wahl für Eins-zu-Eins-Beziehungen mit einem klaren Vertrag (tableView.delegate). Delegate ist schneller und typsicherer. KVO ist die Wahl zur Überwachung von Änderungen einer bestimmten Modelleigenschaft (isLoading, progress). KVO erfordert Vererbung von NSObject und kann Debugging-Schwierigkeiten verursachen (magische String-Schlüssel). In modernem Swift ersetzen Combine und async-Sequenzen alle drei Ansätze.
Die Methode addObserver unterstützt zwei Abonnementvarianten: selector-based (traditionell) und block-based (mit Closure). Selector-based erfordert @objc-Kompatibilität und manuelles Entfernen des Beobachters. Block-based (iOS 9+) ermöglicht die Verwendung einer Capture-List und wird vom OS automatisch verwaltet, wenn Blöcke ohne starke Referenzen verwendet werden. Block-based unterstützt auch queue — der Beobachter erhält die Benachrichtigung in der angegebenen Warteschlange.
Die traditionelle Methode des Abonnierens über einen Selector. Die Handler-Methode muss mit @objc markiert sein und ein optionales Notification akzeptieren. Vorteil: Kann von jeder Klasse verwendet werden, einschließlich Legacy-Objective-C. Nachteile: Fehlende Typsicherheit des Selectors, Risiko von Tippfehlern im Selectornamen, obligatorisches removeObserver in deinit. Wenn der Beobachter vor dem Objekt entfernt wird, wird der Handler nicht aufgerufen.
Die block-based API akzeptiert einen Closure, der beim Empfang der Benachrichtigung ausgeführt wird. Der queue-Parameter bestimmt, in welcher Warteschlange der Block ausgeführt wird — der Hauptwarteschlange für UI-Updates oder einer Hintergrundwarteschlange für die Datenverarbeitung. Der Rückgabewert NSObjectProtocol wird zum Entfernen des Beobachters verwendet: NotificationCenter.default.removeObserver(observer). Block-based wird in modernem Swift bevorzugt.
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)
}
}
// Verwendung mit automatischer Entfernung
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() }
}
}
Speicherlecks sind eines der Hauptprobleme bei der Arbeit mit NotificationCenter. Wenn ein Beobachter vor der Freigabe nicht entfernt wird, versucht das Center beim Senden einer Benachrichtigung, eine Methode auf einem bereits freigegebenen Objekt aufzurufen, was zu EXC_BAD_ACCESS führt. Seit iOS 9 verwendet block-based addObserver schwache Referenzen, aber selector-based erfordert weiterhin manuelles removeObserver. Best Practice: Entfernen Sie den Beobachter in deinit.
Selector-based: Rufen Sie immer NotificationCenter.default.removeObserver(self) in deinit auf. Wenn der Beobachter mehrere Benachrichtigungen abonniert hat, können Sie alle auf einmal (ohne Parameter) oder eine bestimmte nach Namen entfernen. Block-based: Entfernen Sie über removeObserver mit dem von addObserver zurückgegebenen Token. Für block-based auf iOS 9+ tritt kein Speicherleck auf, aber die Entfernung wird dennoch aus Leistungsgründen empfohlen: Freigegebene Beobachter werden während post nicht durchlaufen.
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() { }
}
Das Token-Muster automatisiert die Beobachterverwaltung. Beim Abonnieren wird ein Token-Objekt (NSObjectProtocol) zurückgegeben, das bei Freigabe automatisch den Beobachter entfernt. NotificationTokenWrapper speichert eine schwache Referenz auf NotificationCenter und den Beobachter-Token und ruft removeObserver in deinit auf. Dies bringt NotificationCenter näher an den Combine-Ansatz, bei dem AnyCancellable den Abonnement-Lebenszyklus verwaltet.
Threadsicherheit: NotificationCenter garantiert, dass post von jedem Thread aus aufgerufen werden kann und alle Beobachter die Benachrichtigung im selben Thread erhalten, in dem post aufgerufen wurde. Dies ist kritisch für Multithread-Anwendungen: Wenn eine Benachrichtigung aus einem Hintergrundthread gesendet wird, werden die Handler ebenfalls im Hintergrundthread ausgeführt. Für UI-Updates dispatchen Sie die Verarbeitung über DispatchQueue.main.async an die Hauptwarteschlange.
NotificationCenter ist threadsicher für post- und addObserver-Aufrufe aus verschiedenen Threads. Die interne Synchronisation verwendet Sperren, daher können häufige posts aus mehreren Threads zu Konflikten führen. Für Hochlastszenarien (Download-Fortschritt von 1000 Dateien) verwenden Sie eine separate Benachrichtigungswarteschlange oder einen Combine-Publisher. NotificationQueue mit postingStyle .now entspricht direktem post.
NotificationCenter unterstützt Combine-Publisher über NotificationCenter.default.publisher(for:object:). Publisher verwandelt jede Benachrichtigung in ein Combine-Ereignis, das durch map, filter, debounce und throttle transformiert werden kann. Dies löst das Problem der synchronen Zustellung: Combine verarbeitet Benachrichtigungen asynchron auf dem angegebenen Scheduler. NotificationCenter.publisher ist eine Brücke zwischen dem Legacy-Mechanismus und moderner reaktiver Programmierung.
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)
}
}
Häufig gestellte Fragen
Ja, NotificationCenter ist threadsicher für post- und addObserver-Aufrufe aus jedem Thread. Allerdings werden die Handler im selben Thread ausgeführt, in dem post aufgerufen wurde. Für UI-Updates verwenden Sie queue: .main im block-based addObserver oder DispatchQueue.main.async innerhalb des Handlers. Combine-Publisher mit receive(on:) löst ebenfalls das Thread-Problem.
Selector-based: Crash EXC_BAD_ACCESS beim Senden einer Benachrichtigung nach der Freigabe des Beobachters. Block-based (iOS 9+): Kein Speicherleck dank schwacher Referenz, aber das Benachrichtigungscenter behält den Block bis zum expliziten removeObserver im Speicher. Es wird empfohlen, den Beobachter immer in deinit zu entfernen oder das Token-Muster zur automatischen Verwaltung zu verwenden.
NotificationCenter ist ein Broadcast-Mechanismus für beliebige Ereignisse zwischen nicht verwandten Komponenten. KVO beobachtet Änderungen einer bestimmten Eigenschaft eines bestimmten Objekts. KVO erfordert NSObject-Vererbung und benachrichtigt automatisch bei Eigenschaftsänderungen über setter. NotificationCenter benachrichtigt nur bei explizitem post-Aufruf. Für die Modellbeobachtung ist KVO oder Combine vorzuziehen.
Ein default center pro Anwendungsprozess. Zusätzliche Center können über NotificationCenter() erstellt werden, aber in der Praxis wird das gemeinsame default verwendet. Jedes Center arbeitet unabhängig — post in einem wird nicht an Beobachter eines anderen zugestellt. Verwenden Sie zur Modulisolierung separate Name-Namensräume durch Reverse-Domain-Benachrichtigungsnamen.
Teilweise. Combine stellt NotificationCenter.Publisher bereit, der NotificationCenter in einen reaktiven Strom einhüllt. Combine löst das Synchronitätsproblem (über receive(on:)), fügt Transformationsoperatoren und automatische Abonnementverwaltung (AnyCancellable) hinzu. NotificationCenter bleibt jedoch für iOS-Systembenachrichtigungen (UIApplication, UIKeyboard) und Legacy-Code bestehen. Combine ist eine Erweiterung, kein Ersatz.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch