NotificationCenter — essenza, principio di funzionamento e architettura delle notifiche

Autore: IT Sectr Pubblicato: 2026-03-18 Tempo di lettura: 10 min

NotificationCenter è un meccanismo di sistema in iOS per inviare e ricevere notifiche tra componenti dell'applicazione senza una connessione diretta tra mittente e destinatario. Basato sul pattern Observer, NotificationCenter consente agli oggetti di iscriversi agli eventi e rispondere ad essi in modo asincrono. Secondo Apple Documentation (2025), NSNotificationCenter supporta sia l'invio sincrono di notifiche tramite post(name:object:) sia l'invio differito tramite NotificationQueue. Il centro notifiche opera all'interno di un singolo processo e non oltrepassa i confini dell'applicazione.

Punti chiave

  • NotificationCenter — un'implementazione del pattern Observer per lo scambio di eventi tra componenti iOS.
  • addObserver iscrive un oggetto a notifiche con un nome specifico e un oggetto mittente.
  • post(name:object:) invia una notifica a tutti gli osservatori iscritti in modo sincrono.
  • removeObserver deve essere chiamato in deinit, altrimenti si verifica un crash all'invio della notifica.
  • NotificationQueue consente di differire le notifiche per la consegna asincrona.

Cos'è NotificationCenter?

NotificationCenter (NSNotificationCenter) è un meccanismo integrato di iOS per implementare una comunicazione debolmente accoppiata tra oggetti. Il pattern Observer consente a un oggetto (mittente) di notificare a più altri oggetti (osservatori) un evento senza un riferimento diretto ad essi. NotificationCenter opera con tre entità: Notification.Name (identificatore di notifica), Notification (contenitore con dati) e NotificationCenter (dispacciatore). Ogni applicazione ha un default center condiviso.

NSNotification e Notification.Name

Notification.Name è una struttura che identifica il tipo di notifica. Creata tramite extension Name: Notification.Name(“MyNotification”). Notification è un oggetto che contiene name, object (mittente) e userInfo (dizionario con dati). Le notifiche di sistema sono dichiarate come costanti: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Le notifiche personalizzate dovrebbero essere raggruppate tramite extension per evitare collisioni di nomi. I nomi dovrebbero essere a dominio inverso.

swift
// Definizione di notifiche personalizzate
extension Notification.Name {
    static let dataDidUpdate =
        Notification.Name("com.app.dataDidUpdate")
    static let userLoggedOut =
        Notification.Name("com.app.userLoggedOut")
}

// Invio di una notifica con dati
let userInfo: [String: Any] = [
    "userId": 123,
    "timestamp": Date()
]
NotificationCenter.default.post(
    name: .dataDidUpdate,
    object: nil,
    userInfo: userInfo
)

Aggiungere un osservatore (addObserver)

Un osservatore si iscrive a una notifica tramite il metodo addObserver(_:selector:name:object:). Selector è il metodo che verrà chiamato alla ricezione della notifica. Il parametro object consente di filtrare le notifiche da un mittente specifico. Se object è nil, l'osservatore riceve tutte le notifiche con il nome specificato da qualsiasi mittente. Da iOS 9, addObserver non richiede la rimozione manuale per l'API block-based, ma selector-based richiede ancora removeObserver.

swift
// Iscrizione a una notifica (basata su selettore)
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)
}

// Iscrizione a una notifica (basata su blocco, 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)
}

Come funziona NotificationCenter?

NotificationCenter memorizza una tabella di mappatura (nome → insieme di osservatori). Quando il mittente chiama post(name:object:), il centro notifiche scorre sincronamente tutti gli osservatori iscritti a quel nome e chiama i loro selettori o blocchi. Caratteristica chiave: post blocca il thread corrente fino al completamento di tutti i gestori. Se i gestori eseguono operazioni pesanti, ciò ritarda il mittente. NotificationQueue risolve questo problema differendo la consegna delle notifiche.

Invio sincrono (post)

Il metodo post(name:object:userInfo:) invia una notifica immediatamente a tutti gli osservatori. La chiamata è sincrona — il codice dopo post viene eseguito solo dopo il completamento di tutti i gestori. L'ordine di invocazione degli osservatori non è garantito e può cambiare tra esecuzioni. Per l'elaborazione sequenziale, utilizzare NotificationQueue con coalescing. Non chiamare post all'interno di un gestore della stessa notifica — si verifica ricorsione infinita.

Invio differito (NotificationQueue)

NotificationQueue aggiunge notifiche a una coda per la consegna asincrona. Supporta il coalescing (unione di notifiche identiche) e la selezione della coda di consegna (asap, idle, modal). Il coalescing è utile per eventi frequenti (avanzamento download) quando è necessario notificare solo con l'ultimo valore. NotificationQueue utilizza il run loop per attivarsi, quindi funziona solo in thread con run loop attivo.

swift
// Invio differito tramite NotificationQueue
let notification = Notification(
    name: .dataDidUpdate,
    object: self,
    userInfo: ["progress": 0.5]
)

// Coalescing: più notifiche vengono unite in una
NotificationQueue.default.enqueue(
    notification,
    postingStyle: .whenIdle,
    coalesceMask: .onName,
    forModes: [.common]
)

// Consegna asincrona tramite DispatchQueue
DispatchQueue.main.async {
    NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}

Notification vs Delegate vs KVO

iOS fornisce tre meccanismi principali per la comunicazione tra oggetti: NotificationCenter, Delegate e KVO (Key-Value Observing). Ciascuno risolve il problema della notifica ma con diversi compromessi in termini di accoppiamento, prestazioni e sicurezza dei tipi. La scelta del meccanismo dipende dalla relazione uno-a-uno o uno-a-molti e dalla necessità di trasferimento dati.

CaratteristicaNotificationCenterDelegateKVO
AccoppiamentoDebole (nome notifica)Forte (protocollo)Medio (chiave)
RelazioneUno-a-moltiUno-a-unoUno-a-molti
Sicurezza dei tipiBassa (userInfo come Dictionary)Alta (metodi del protocollo)Media (Any?)
PrestazioniMedie (scorrimento tabella)Alte (chiamata diretta)Basse (NSObject)
AsincroniaSincrono (post blocca)Sincrono nel thread del mittenteSincrono al cambiamento

Quando scegliere NotificationCenter

NotificationCenter è ideale per eventi a cui più componenti indipendenti devono rispondere. Esempi: modifiche alle impostazioni dell'app, logout dell'utente, ricezione di una notifica push in background. NotificationCenter è adatto anche per moduli debolmente accoppiati (la funzionalità A non deve conoscere la funzionalità B). Lo svantaggio è la mancanza di sicurezza dei tipi: le chiavi userInfo sono stringhe, non enumerazioni.

Quando scegliere Delegate o KVO

Delegate è la scelta per relazioni uno-a-uno con un contratto chiaro (tableView.delegate). Delegate è più veloce e sicuro per tipi. KVO è la scelta per osservare modifiche a una proprietà specifica del modello (isLoading, progress). KVO richiede l'ereditarietà da NSObject e può causare difficoltà di debug (stringhe magiche come chiavi). In Swift moderno, Combine e le sequenze async sostituiscono tutti e tre gli approcci.

AddObserver: notifiche sincrone e asincrone

Il metodo addObserver supporta due varianti di iscrizione: selector-based (tradizionale) e block-based (con closure). Selector-based richiede compatibilità @objc e rimozione manuale dell'osservatore. Block-based (iOS 9+) consente di utilizzare una capture list e viene gestito automaticamente dal SO quando si utilizzano blocchi senza riferimenti forti. Block-based supporta anche queue — l'osservatore riceve la notifica nella coda specificata.

addObserver basato su selettore

Il modo tradizionale di iscrizione tramite selettore. Il metodo gestore deve essere contrassegnato con @objc e accettare una Notification opzionale. Vantaggio: può essere utilizzato da qualsiasi classe, incluso Objective-C legacy. Svantaggi: mancanza di sicurezza dei tipi del selettore, rischio di errori di battitura nel nome del selettore, removeObserver obbligatorio in deinit. Se l'osservatore viene rimosso prima dell'oggetto, il gestore non verrà chiamato.

addObserver basato su blocco

L'API block-based accetta una closure che viene eseguita alla ricezione della notifica. Il parametro queue determina in quale coda viene eseguito il blocco — la coda principale per gli aggiornamenti dell'interfaccia o una coda in background per l'elaborazione dei dati. Il valore restituito NSObjectProtocol viene utilizzato per rimuovere l'osservatore: NotificationCenter.default.removeObserver(observer). Block-based è preferibile in Swift moderno.

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

// Utilizzo con rimozione automatica
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() }
    }
}

Gestione della memoria e rimozione degli osservatori

Le perdite di memoria sono uno dei problemi principali quando si lavora con NotificationCenter. Se un osservatore non viene rimosso prima della deallocazione, all'invio di una notifica il centro tenterà di chiamare un metodo su un oggetto già deallocato, causando EXC_BAD_ACCESS. Da iOS 9, block-based addObserver utilizza riferimenti deboli, ma selector-based richiede ancora removeObserver manuale. Buona pratica: rimuovere l'osservatore in deinit.

Quando chiamare removeObserver

Selector-based: chiamare sempre NotificationCenter.default.removeObserver(self) in deinit. Se l'osservatore è iscritto a più notifiche, è possibile rimuoverle tutte in una volta (senza parametri) o una specifica per nome. Block-based: rimuovere tramite removeObserver con il token restituito da addObserver. Per block-based su iOS 9+, non si verifica perdita, ma la rimozione è comunque raccomandata per prestazioni: gli osservatori deallocati non verranno attraversati durante post.

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

Riferimenti deboli tramite pattern Token

Il pattern Token automatizza la gestione degli osservatori. All'iscrizione, viene restituito un oggetto token (NSObjectProtocol) che rimuove automaticamente l'osservatore alla deallocazione. NotificationTokenWrapper memorizza un riferimento debole a NotificationCenter e al token dell'osservatore, chiamando removeObserver in deinit. Questo avvicina NotificationCenter all'approccio Combine, dove AnyCancellable gestisce il ciclo di vita dell'iscrizione.

NotificationCenter in ambiente multithread

Sicurezza dei thread: NotificationCenter garantisce che post possa essere chiamato da qualsiasi thread e tutti gli osservatori riceveranno la notifica nello stesso thread in cui è stato chiamato post. Questo è critico per applicazioni multithread: se una notifica viene inviata da un thread in background, anche i gestori verranno eseguiti nel thread in background. Per gli aggiornamenti dell'interfaccia, distribuire la gestione alla coda principale tramite DispatchQueue.main.async.

Sicurezza dei thread di post e addObserver

NotificationCenter è thread-safe per chiamate post e addObserver da thread diversi. La sincronizzazione interna utilizza blocchi, quindi post frequenti da più thread possono creare contesa. Per scenari ad alto carico (avanzamento download di 1000 file), utilizzare una coda di notifiche separata o un publisher Combine. NotificationQueue con postingStyle .now equivale a post diretto.

Consegna asincrona tramite Combine

NotificationCenter supporta il publisher Combine tramite NotificationCenter.default.publisher(for:object:). Publisher trasforma ogni notifica in un evento Combine che può essere trasformato tramite map, filter, debounce e throttle. Questo risolve il problema della consegna sincrona: Combine elabora le notifiche in modo asincrono sul Scheduler specificato. NotificationCenter.publisher è un ponte tra il meccanismo legacy e la programmazione reattiva moderna.

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

Domande frequenti

NotificationCenter è thread-safe?

, NotificationCenter è thread-safe per chiamate post e addObserver da qualsiasi thread. Tuttavia, i gestori vengono eseguiti nello stesso thread in cui è stato chiamato post. Per gli aggiornamenti dell'interfaccia, utilizzare queue: .main in block-based addObserver o DispatchQueue.main.async all'interno del gestore. Il publisher Combine con receive(on:) risolve anche il problema del thread.

Cosa succede se non rimuovo l'osservatore?

Selector-based: crash EXC_BAD_ACCESS all'invio di una notifica dopo la deallocazione dell'osservatore. Block-based (iOS 9+): nessuna perdita grazie al riferimento debole, ma il centro notifiche continua a mantenere il blocco in memoria fino a removeObserver esplicito. Si consiglia di rimuovere sempre l'osservatore in deinit o utilizzare il pattern Token per la gestione automatica.

Qual è la differenza tra NotificationCenter e KVO?

NotificationCenter è un meccanismo di broadcast per eventi arbitrari tra componenti non correlati. KVO osserva modifiche a una proprietà specifica di un oggetto specifico. KVO richiede l'ereditarietà da NSObject e notifica automaticamente al cambiamento della proprietà tramite setter. NotificationCenter notifica solo quando post viene chiamato esplicitamente. Per l'osservazione del modello, è preferibile KVO o Combine.

Quanti NotificationCenter esistono in un'applicazione?

Un default center per processo applicativo. Centri aggiuntivi possono essere creati tramite NotificationCenter(), ma in pratica viene utilizzato il default condiviso. Ogni centro opera indipendentemente — post in uno non viene consegnato agli osservatori di un altro. Per l'isolamento dei moduli, utilizzare spazi dei nomi Name separati tramite nomi di notifica a dominio inverso.

Combine sostituisce NotificationCenter?

Parzialmente. Combine fornisce NotificationCenter.Publisher, che avvolge NotificationCenter in un flusso reattivo. Combine risolve il problema di sincronia (tramite receive(on:)), aggiunge operatori di trasformazione e gestione automatica delle iscrizioni (AnyCancellable). Tuttavia, NotificationCenter rimane per le notifiche di sistema iOS (UIApplication, UIKeyboard) e codice legacy. Combine è un miglioramento, non una sostituzione.

Riepilogo

  • NotificationCenter — un'implementazione del pattern Observer per comunicazione debolmente accoppiata uno-a-molti in iOS.
  • post invia una notifica in modo sincrono a tutti gli osservatori nel thread corrente, bloccando il mittente.
  • addObserver supporta iscrizione selector-based (con @objc) e block-based (con capture list e queue).
  • removeObserver è obbligatorio in deinit per iscrizioni selector-based, altrimenti crash.
  • NotificationQueue fornisce consegna differita con coalescing per eventi frequenti.
  • Sicurezza dei thread garantisce il funzionamento da qualsiasi thread, ma i gestori vengono eseguiti nel thread del mittente.
  • Utilizzare il pattern Token o il publisher Combine per una gestione delle iscrizioni sicura e moderna.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche