NotificationCenter — är en systemmekanism i iOS för att skicka och ta emot notifikationer mellan applikationskomponenter utan direkt koppling mellan avsändare och mottagare. Baserat på Observer-mönstret låter NotificationCenter objekt prenumerera på händelser och reagera asynkront. Enligt Apple Documentation (2025) stöder NSNotificationCenter både synkron sändning av notifikationer via post(name:object:) och fördröjd sändning via NotificationQueue. Notifikationscentret fungerar inom en process och korsar inte applikationsgränser.
Huvudpunkter
NotificationCenter (NSNotificationCenter) — är en inbyggd iOS-mekanism för att implementera löst kopplad kommunikation mellan objekt. Observer-mönstret låter ett objekt (avsändare) meddela flera andra objekt (observatörer) om en händelse utan direkt referens till dem. NotificationCenter arbetar med tre entiteter: Notification.Name (notifikationsidentifierare), Notification (behållare med data) och NotificationCenter (dispatcher). Varje applikation har ett delat default center.
Notification.Name — är en struktur som identifierar notifikationstypen. Skapas via extension Name: Notification.Name("MyNotification"). Notification — är ett objekt som innehåller name, object (avsändare) och userInfo (ordbok med data). Systemnotifikationer deklareras som konstanter: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Anpassade notifikationer bör grupperas via extension för att undvika namnkollisioner. Namn bör vara omvänt-domän.
// Definiera anpassade notifikationer
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Skicka notifikation med data
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
Observatören prenumererar på en notifikation via metoden addObserver(_:selector:name:object:). Selector — metoden som anropas när notifikationen tas emot. Parametern object gör det möjligt att filtrera notifikationer från en specifik avsändare. Om object är nil tar observatören emot alla notifikationer med angivet namn från alla avsändare. Från och med iOS 9 kräver addObserver inte manuell borttagning för block-based API, men selector-based kräver fortfarande removeObserver.
// Prenumerera på notifikation (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)
}
// Prenumerera på notifikation (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 lagrar en mappningstabell (name → uppsättning observatörer). När avsändaren anropar post(name:object:) går notifikationscentret synkront igenom alla observatörer som prenumererar på detta namn och anropar deras selektorer eller block. Nyckelegenskap: post blockerar den aktuella tråden tills alla handlare är klara. Om handlare utför tunga operationer fördröjer detta avsändaren. NotificationQueue löser detta problem genom att fördröja notifikationsleveransen.
Metoden post(name:object:userInfo:) skickar notifikationen omedelbart till alla observatörer. Anropet är synkront — koden efter post körs först efter att alla handlare har slutförts. Anropsordningen för observatörer är inte garanterad och kan ändras mellan körningar. För sekventiell bearbetning, använd NotificationQueue med coalescing. Anropa inte post inuti en handlare för samma notifikation — detta leder till oändlig rekursion.
NotificationQueue lägger till notifikationer i en kö för asynkron leverans. Stöder coalescing (sammanslagning av identiska notifikationer) och val av leveranskö (asap, idle, modal). Coalescing är användbart för frekventa händelser (laddningsförlopp), när endast det senaste värdet behöver meddelas. NotificationQueue använder run loop för aktivering, så fungerar endast i trådar med aktiv run loop.
// Fördröjd sändning via NotificationQueue
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: flera notifikationer slås samman till en
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Asynkron sändning via DispatchQueue
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
iOS tillhandahåller tre huvudmekanismer för kommunikation mellan objekt: NotificationCenter, Delegate och KVO (Key-Value Observing). Var och en löser notifikationsproblemet, men med olika avvägningar gällande koppling, prestanda och typsäkerhet. Valet av mekanism beror på relationen "en-till-en" eller "en-till-många" och behovet av dataöverföring.
| Egenskap | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Koppling | Lös (notifikationsnamn) | Stark (protokoll) | Medel (nyckel) |
| Relation | En-till-många | En-till-en | En-till-många |
| Typsäkerhet | Låg (userInfo som Dictionary) | Hög (protokollmetoder) | Medel (Any?) |
| Prestanda | Medel (tabellgenomgång) | Hög (direkt anrop) | Låg (NSObject) |
| Asynkronicitet | Synkront (post blockerar) | Synkront i avsändarens tråd | Synkront vid ändring |
NotificationCenter är idealiskt för händelser som flera oberoende komponenter måste reagera på. Exempel: ändring av appinställningar, användarens utloggning, mottagning av push-notifikation i bakgrunden. NotificationCenter passar även för löst kopplade moduler (funktion A behöver inte känna till funktion B). Nackdel — ingen typsäkerhet: userInfo-nycklar är strängar, inte enum.
Delegate välj för en-till-en-relation med tydligt kontrakt (tableView.delegate). Delegate är snabbare och typsäkrare. KVO välj för att observera ändring av en specifik egenskap i en modell (isLoading, progress). KVO kräver arv från NSObject och kan orsaka svårigheter vid felsökning (magiska nyckelsträngar). I modern Swift ersätter Combine och async sequences alla tre tillvägagångssätten.
Metoden addObserver stöder två prenumerationsvarianter: selector-based (traditionell) och block-based (med closure). Selector-based kräver @objc-kompatibilitet och manuell borttagning av observatören. Block-based (iOS 9+) möjliggör användning av capture list och hanteras automatiskt av OS när block används utan starka referenser. Block-based stöder även queue — observatören tar emot notifikationen i den angivna kön.
Traditionellt sätt att prenumerera via selektor. Handlermetoden måste markeras med @objc och ta emot en valfri Notification. Fördel: kan användas av vilken klass som helst, inklusive legacy Objective-C. Nackdelar: brist på typsäkerhet för selektorn, risk för stavfel i selektorns namn, obligatorisk removeObserver i deinit. Om observatören tas bort före objektet anropas inte handlern.
Block-based API tar emot en closure som körs när notifikationen tas emot. Parametern queue bestämmer i vilken kön blocket körs — main queue för UI-uppdateringar eller background queue för databearbetning. Det returnerade NSObjectProtocol-värdet används för att ta bort observatören: NotificationCenter.default.removeObserver(observer). Block-based föredras i 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)
}
}
// Användning med automatisk borttagning
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() }
}
}
Minnesläckor — ett av huvudproblemen vid arbete med NotificationCenter. Om observatören inte tas bort före deallokering kommer centret vid sändning av notifikation att försöka anropa metoden för det redan frigjorda objektet, vilket leder till EXC_BAD_ACCESS. Från och med iOS 9 använder block-based addObserver svaga referenser, men selector-based kräver fortfarande manuell removeObserver. Best practice: ta bort observatören i deinit.
Selector-based: anropa obligatoriskt NotificationCenter.default.removeObserver(self) i deinit. Om observatören prenumererar på flera notifikationer kan alla tas bort på en gång (utan parametrar) eller en specifik efter namn. Block-based: ta bort via removeObserver med token från addObserver. För block-based på iOS 9+ uppstår ingen läcka, men borttagning rekommenderas ändå för prestanda: frigjorda observatörer kommer inte att genomsökas vid 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() { }
}
Token-mönstret automatiserar hanteringen av observatörer. Vid prenumeration returneras ett token-objekt (NSObjectProtocol) som vid deallokering automatiskt tar bort observatören. NotificationTokenWrapper lagrar en svag referens till NotificationCenter och observatörstoken, och anropar removeObserver i deinit. Detta närmar NotificationCenter till Combine-metoden, där AnyCancellable hanterar prenumerationens livscykel.
Thread safety NotificationCenter garanterar att post kan anropas från vilken tråd som helst och alla observatörer får notifikationen i samma tråd där post anropades. Detta är kritiskt för flertrådsapplikationer: om notifikationen skickades från en bakgrundstråd kommer handlare också att köras i bakgrundstråden. För UI-uppdatering måste bearbetningen skickas till main queue via DispatchQueue.main.async.
NotificationCenter är trådsäkert för anrop av post och addObserver från olika trådar. Intern synkronisering använder låsning, så frekventa post från flera trådar kan skapa contention. För scenarier med hög belastning (laddningsförlopp för 1000 filer) använd en separat notifikationskö eller Combine publisher. NotificationQueue med postingStyle .now motsvarar direkt post.
NotificationCenter stöder Combine publisher via NotificationCenter.default.publisher(for:object:). Publisher omvandlar varje notifikation till en Combine-händelse som kan transformeras via map, filter, debounce och throttle. Detta löser problemet med synkron leverans: Combine bearbetar notifikationer asynkront på en angiven Scheduler. NotificationCenter.publisher — är en bro mellan legacy-mekanismen och modern reaktiv programmering.
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)
}
}
Vanliga frågor
Ja, NotificationCenter är trådsäkert för anrop av post och addObserver från vilka trådar som helst. Dock körs handlare i samma tråd där post anropades. För UI-uppdatering använd queue: .main i block-based addObserver eller DispatchQueue.main.async inuti handlern. Combine publisher med receive(on:) löser också trådproblemet.
Selector-based: krasch EXC_BAD_ACCESS vid sändning av notifikation efter deallokering av observatören. Block-based (iOS 9+): ingen läcka tack vare svag referens, men notifikationscentret fortsätter att hålla blocket i minnet tills explicit removeObserver. Rekommenderas att alltid ta bort observatören i deinit eller använda Token-mönstret för automatisk hantering.
NotificationCenter — sändning av godtyckliga händelser mellan orelaterade komponenter. KVO — observation av ändring av en specifik egenskap hos ett specifikt objekt. KVO kräver arv från NSObject och meddelar automatiskt vid egenskapsändring via setter. NotificationCenter meddelar endast vid explicit anrop av post. För modellobservation är KVO eller Combine att föredra.
Ett default center per applikationsprocess. Ytterligare center kan skapas via NotificationCenter(), men i praktiken används det delade default. Varje center fungerar oberoende — post i ett center levereras inte till observatörer i ett annat. För modulisolering, använd separata Name-namnrymder via omvänt-domän notifikationsnamn.
Delvis. Combine tillhandahåller NotificationCenter.Publisher som omsluter NotificationCenter i en reaktiv ström. Combine löser synkronitetsproblemet (via receive(on:)), lägger till transformationsoperatorer och automatisk prenumerationshantering (AnyCancellable). Dock kvarstår NotificationCenter för iOS-systemnotifikationer (UIApplication, UIKeyboard) och legacy-kod. Combine är ett tillägg, inte en ersättning.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också