NotificationCenter é um mecanismo do sistema no iOS para enviar e receber notificações entre componentes do aplicativo sem uma conexão direta entre remetente e destinatário. Baseado no padrão Observer, o NotificationCenter permite que objetos se inscrevam em eventos e respondam a eles de forma assíncrona. De acordo com a Apple Documentation (2025), o NSNotificationCenter suporta tanto o envio síncrono de notificações via post(name:object:) quanto o envio adiado via NotificationQueue. O centro de notificações opera dentro de um único processo e não cruza os limites dos aplicativos.
Principais pontos
NotificationCenter (NSNotificationCenter) é um mecanismo integrado do iOS para implementar comunicação fracamente acoplada entre objetos. O padrão Observer permite que um objeto (remetente) notifique vários outros objetos (observadores) sobre um evento sem uma referência direta a eles. O NotificationCenter opera com três entidades: Notification.Name (identificador de notificação), Notification (contêiner com dados) e NotificationCenter (despachante). Cada aplicativo tem um default center compartilhado.
Notification.Name é uma estrutura que identifica o tipo de notificação. Criada via extension Name: Notification.Name(“MyNotification”). Notification é um objeto que contém name, object (remetente) e userInfo (dicionário com dados). As notificações do sistema são declaradas como constantes: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification. Notificações personalizadas devem ser agrupadas via extension para evitar colisões de nomes. Os nomes devem ser de domínio reverso.
// Definição de notificações personalizadas
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// Envio de uma notificação com dados
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
Um observador se inscreve em uma notificação através do método addObserver(_:selector:name:object:). Selector é o método que será chamado quando a notificação for recebida. O parâmetro object permite filtrar notificações de um remetente específico. Se object for nil, o observador recebe todas as notificações com o nome especificado de qualquer remetente. Desde o iOS 9, o addObserver não requer remoção manual para a API block-based, mas o selector-based ainda requer removeObserver.
// Inscrição em uma notificação (baseada em selector)
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)
}
// Inscrição em uma notificação (baseada em bloco, 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)
}
O NotificationCenter armazena uma tabela de mapeamento (nome → conjunto de observadores). Quando o remetente chama post(name:object:), o centro de notificações percorre sincronamente todos os observadores inscritos nesse nome e chama seus seletores ou blocos. Característica chave: post bloqueia a thread atual até que todos os manipuladores concluam. Se os manipuladores realizam operações pesadas, isso atrasa o remetente. O NotificationQueue resolve esse problema adiando a entrega das notificações.
O método post(name:object:userInfo:) envia uma notificação imediatamente a todos os observadores. A chamada é síncrona — o código após post executa somente após todos os manipuladores concluírem. A ordem de invocação dos observadores não é garantida e pode mudar entre execuções. Para processamento sequencial, use NotificationQueue com coalescing. Não chame post dentro de um manipulador da mesma notificação — isso leva à recursão infinita.
NotificationQueue adiciona notificações a uma fila para entrega assíncrona. Suporta coalescing (fusão de notificações idênticas) e seleção de fila de entrega (asap, idle, modal). O coalescing é útil para eventos frequentes (progresso de download) quando você precisa notificar apenas com o último valor. O NotificationQueue usa o run loop para disparar, portanto funciona apenas em threads com run loop ativo.
// Envio adiado via NotificationQueue
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: várias notificações são fundidas em uma
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// Entrega assíncrona via DispatchQueue
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
O iOS fornece três mecanismos principais para comunicação entre objetos: NotificationCenter, Delegate e KVO (Key-Value Observing). Cada um resolve o problema de notificação, mas com diferentes compromissos em acoplamento, desempenho e segurança de tipos. A escolha do mecanismo depende da relação um-para-um ou um-para-muitos e da necessidade de transferência de dados.
| Característica | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| Acoplamento | Fraco (nome da notificação) | Forte (protocolo) | Médio (chave) |
| Relação | Um-para-muitos | Um-para-um | Um-para-muitos |
| Segurança de tipos | Baixa (userInfo como Dictionary) | Alta (métodos do protocolo) | Média (Any?) |
| Desempenho | Médio (percurso da tabela) | Alto (chamada direta) | Baixo (NSObject) |
| Assincronia | Síncrono (post bloqueia) | Síncrono na thread do remetente | Síncrono na alteração |
NotificationCenter é ideal para eventos aos quais múltiplos componentes independentes precisam responder. Exemplos: alterações nas configurações do aplicativo, logout do usuário, recebimento de uma notificação push em segundo plano. NotificationCenter também é adequado para módulos fracamente acoplados (o recurso A não deve conhecer o recurso B). A desvantagem é a falta de segurança de tipos: as chaves do userInfo são strings, não enums.
Delegate é a escolha para relacionamentos um-para-um com um contrato claro (tableView.delegate). Delegate é mais rápido e seguro por tipos. KVO é a escolha para observar mudanças em uma propriedade específica do modelo (isLoading, progress). KVO requer herança de NSObject e pode causar dificuldades de depuração (strings mágicas como chaves). Em Swift moderno, Combine e sequências async substituem todas as três abordagens.
O método addObserver suporta duas variantes de inscrição: selector-based (tradicional) e block-based (com closure). Selector-based requer compatibilidade @objc e remoção manual do observador. Block-based (iOS 9+) permite usar uma capture list e é gerenciado automaticamente pelo SO ao usar blocos sem referências fortes. Block-based também suporta queue — o observador recebe a notificação na fila especificada.
A forma tradicional de inscrição via selector. O método manipulador deve ser marcado com @objc e aceitar uma Notification opcional. Vantagem: pode ser usado por qualquer classe, incluindo Objective-C legado. Desvantagens: falta de segurança de tipos do selector, risco de erros de digitação no nome do selector, removeObserver obrigatório no deinit. Se o observador for removido antes do objeto, o manipulador não será chamado.
A API block-based aceita uma closure que executa ao receber a notificação. O parâmetro queue determina em qual fila o bloco executa — a fila principal para atualizações de UI ou uma fila em segundo plano para processamento de dados. O valor de retorno NSObjectProtocol é usado para remover o observador: NotificationCenter.default.removeObserver(observer). Block-based é preferível em Swift moderno.
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)
}
}
// Uso com remoção automática
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() }
}
}
Vazamentos de memória são um dos principais problemas ao trabalhar com NotificationCenter. Se um observador não for removido antes da desalocação, ao enviar uma notificação o centro tentará chamar um método em um objeto já desalocado, resultando em EXC_BAD_ACCESS. Desde o iOS 9, o block-based addObserver usa referências fracas, mas o selector-based ainda requer removeObserver manual. Melhor prática: remover o observador no deinit.
Selector-based: sempre chame NotificationCenter.default.removeObserver(self) no deinit. Se o observador estiver inscrito em várias notificações, você pode remover todas de uma vez (sem parâmetros) ou uma específica pelo nome. Block-based: remova via removeObserver com o token retornado pelo addObserver. Para block-based no iOS 9+, não ocorre vazamento, mas a remoção ainda é recomendada para desempenho: observadores desalocados não serão percorridos durante o 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() { }
}
O padrão Token automatiza o gerenciamento de observadores. Ao se inscrever, um objeto token (NSObjectProtocol) é retornado, que remove automaticamente o observador ao ser desalocado. NotificationTokenWrapper armazena uma referência fraca ao NotificationCenter e ao token do observador, chamando removeObserver no deinit. Isso aproxima o NotificationCenter da abordagem Combine, onde AnyCancellable gerencia o ciclo de vida da inscrição.
Segurança de threads: o NotificationCenter garante que post pode ser chamado de qualquer thread, e todos os observadores receberão a notificação na mesma thread onde post foi chamado. Isso é crítico para aplicações multithread: se uma notificação for enviada de uma thread em segundo plano, os manipuladores também executarão na thread em segundo plano. Para atualizações de UI, despache o tratamento para a fila principal via DispatchQueue.main.async.
NotificationCenter é seguro para threads para chamadas post e addObserver de diferentes threads. A sincronização interna usa bloqueios, portanto posts frequentes de várias threads podem criar contenção. Para cenários de alta carga (progresso de download de 1000 arquivos), use uma fila de notificações separada ou um publisher Combine. NotificationQueue com postingStyle .now é equivalente a post direto.
NotificationCenter suporta publisher Combine via NotificationCenter.default.publisher(for:object:). Publisher transforma cada notificação em um evento Combine que pode ser transformado através de map, filter, debounce e throttle. Isso resolve o problema de entrega síncrona: o Combine processa notificações de forma assíncrona no Scheduler especificado. NotificationCenter.publisher é uma ponte entre o mecanismo legado e a programação reativa moderna.
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)
}
}
Perguntas frequentes
Sim, NotificationCenter é seguro para threads para chamadas post e addObserver de qualquer thread. No entanto, os manipuladores executam na mesma thread onde post foi chamado. Para atualizações de UI, use queue: .main no block-based addObserver ou DispatchQueue.main.async dentro do manipulador. O publisher Combine com receive(on:) também resolve o problema de thread.
Selector-based: crash EXC_BAD_ACCESS ao enviar uma notificação após a desalocação do observador. Block-based (iOS 9+): sem vazamento graças à referência fraca, mas o centro de notificações continua mantendo o bloco na memória até removeObserver explícito. Recomenda-se sempre remover o observador no deinit ou usar o padrão Token para gerenciamento automático.
NotificationCenter é um mecanismo de transmissão para eventos arbitrários entre componentes não relacionados. KVO observa mudanças em uma propriedade específica de um objeto específico. KVO requer herança de NSObject e notifica automaticamente sobre mudanças de propriedade via setter. NotificationCenter notifica apenas quando post é chamado explicitamente. Para observação de modelo, KVO ou Combine é preferível.
Um default center por processo de aplicativo. Centros adicionais podem ser criados via NotificationCenter(), mas na prática o default compartilhado é usado. Cada centro opera independentemente — post em um não é entregue aos observadores de outro. Para isolamento de módulos, use namespaces Name separados através de nomes de notificação de domínio reverso.
Parcialmente. O Combine fornece NotificationCenter.Publisher, que envolve o NotificationCenter em um fluxo reativo. O Combine resolve o problema de sincronia (via receive(on:)), adiciona operadores de transformação e gerenciamento automático de inscrições (AnyCancellable). No entanto, o NotificationCenter permanece para notificações do sistema iOS (UIApplication, UIKeyboard) e código legado. Combine é um aprimoramento, não uma substituição.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também