Observer — conceitos-chave do padrão de subscrição de alterações

Autor: IT Sectr Publicado: 2026-02-17 Tempo de leitura: 7 min

Observer — um padrão comportamental no qual um objeto (o publicador) notifica múltiplos subscritores sobre mudanças no seu estado. No desenvolvimento móvel, Observer é a base dos mecanismos reativos: a UI subscreve as alterações de dados e atualiza-se automaticamente. O padrão está implementado no NotificationCenter no iOS e no LiveData/Flow no Android. Mais detalhes em Refactoring Guru: Observer.

Pontos principais

  • Observer — um padrão de subscrição: um publicador, múltiplos subscritores
  • NotificationCenter — implementação integrada do Observer no iOS/macOS
  • Flow e LiveData — implementações reativas do Observer no Android
  • Push vs Pull — o publicador pode enviar dados ou notificar sobre eventos
  • Fugas de memória — os subscritores devem cancelar a subscrição para evitar fugas

O que é Observer: a essência do padrão observador

Observer — um padrão comportamental GoF que define uma dependência um-para-muitos entre objetos. Quando um objeto (Subject ou Observable) muda o seu estado, todos os objetos dependentes (Observers) são automaticamente notificados e atualizados. O padrão implementa acoplamento fraco: o publicador não conhece as classes específicas dos subscritores — apenas que implementam a interface Observer.

Estrutura do Observer inclui a interface Subject com os métodos attach(), detach(), notify() e a interface Observer com o método update(). ConcreteSubject armazena o estado e a lista de subscritores. ConcreteObserver implementa update() e reage às mudanças. No desenvolvimento móvel, a implementação clássica do GoF é rara — é substituída por mecanismos integrados: NotificationCenter, Combine, Flow, LiveData, que implementam a mesma ideia com APIs modernas.

Modelo Push vs Pull — no modelo Push, o Subject envia dados a todos os subscritores (NotificationCenter.post). No modelo Pull, o Subject apenas notifica, e o subscritor solicita os dados por si próprio. Android LiveData usa Push (os dados são passados no observe()), RxJava/Flow suportam ambos os modelos. A escolha depende da tarefa: Push é mais simples para atualizações de UI, Pull é mais eficiente para grandes volumes de dados que o subscritor pode não querer receber.

Observer no iOS: NotificationCenter, Combine e KVO

NotificationCenter — um mecanismo integrado do iOS/macOS para implementar Observer. O publicador envia uma Notification através de NotificationCenter.default.post(name:, object:, userInfo:). O subscritor regista-se através de addObserver(forName:, queue:, using:). NotificationCenter suporta notificações nomeadas (Notification.Name) e pode passar qualquer dados no userInfo. UIKeyboardWillShowNotification, UIApplicationDidEnterBackgroundNotification — exemplos do sistema.

swift
extension Notification.Name {
    static let userDidLogin = Notification.Name("userDidLogin")
}

// Publicador
NotificationCenter.default.post(
    name: .userDidLogin,
    object: nil,
    userInfo: ["userId": "123"]
)

// Subscritor
class ProfileViewModel {
    private var observers: [NSObjectProtocol] = []

    func startObserving() {
        let observer = NotificationCenter.default.addObserver(
            forName: .userDidLogin,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let userId = notification.userInfo?["userId"] as? String else { return }
            // O subscritor reage ao evento
            self?.loadProfile(userId: userId)
        }
        observers.append(observer)
    }

    func stopObserving() {
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers.removeAll()
    }
}

Framework Combine — uma alternativa reativa moderna ao NotificationCenter, introduzida no iOS 13. Publisher (NotificationCenter, URLSession, Timer) — o publicador, Subscriber (sink, assign) — o subscritor. Combine adiciona operadores (map, filter, combineLatest) para transformação de fluxos de dados. @Published — um property wrapper que notifica automaticamente os subscritores sobre alterações. No MVVM com SwiftUI, Combine substitui o NotificationCenter para ligar ViewModel e View.

KVO (Key-Value Observing) — um mecanismo antigo do ObjC/Swift para observar propriedades individuais de objetos. @objc dynamic var name: String — uma propriedade observável. observe(.name) — subscrição. KVO funciona apenas com classes compatíveis com @objc e herança do ObjC. A Apple recomenda Combine e @Published em vez de KVO em novos projetos. KVO continua relevante para compatibilidade com UIKit em projetos híbridos.

Observer no Android: LiveData, StateFlow e SharedFlow

LiveData — um componente do Android Architecture Components para implementar Observer. Uma classe observável que notifica os subscritores sobre alterações de dados. LiveData tem consciência do ciclo de vida: os subscritores (LifecycleOwner) cancelam automaticamente a subscrição quando são destruídos. LiveData usa o modelo Push: os dados são passados no observe(). LiveData é o bloco de construção básico do MVVM no Android antes do Jetpack Compose.

kotlin
// ViewModel — publicador
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User?>(null)
    val user: LiveData<User?> = _user

    fun loadUser(id: String) {
        viewModelScope.launch {
            val result = userRepository.getUser(id)
            _user.value = result
        }
    }
}

// Fragment — subscritor
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // O subscritor reage às alterações
            userName.text = user?.name
            userEmail.text = user?.email
        }
    }
}

StateFlow e SharedFlow — tipos reativos do Kotlin Coroutines que substituíram o LiveData no Jetpack Compose. StateFlow — um contentor de estado observável com um valor atual fixo. SharedFlow — um fluxo hot configurável sem estado, adequado para eventos únicos (navegação, toasts). Ambos os tipos estão estreitamente integrados com o Compose: collectAsState(), collectAsEffect(). StateFlow é obrigatório em projetos Android modernos com Compose.

LiveData vs StateFlow — LiveData está ligado ao ciclo de vida do Android, StateFlow é independente da plataforma. StateFlow suporta corrotinas, operadores (map, filter) e pode ser testado sem dependências do Android. LiveData é mais simples para compatibilidade com Java. A Google recomenda StateFlow para novos projetos em Kotlin + Compose, LiveData para manter projetos legados ou código Java.

Gestão de subscrições e fugas de memória

Fugas de memória — o principal problema do Observer sem uma gestão adequada de subscrições. Se um subscritor (Activity, Fragment, UIViewController) for destruído mas a subscrição não for cancelada, o publicador continua a manter uma referência para ele e o coletor de lixo não consegue libertar a memória. No Android, LifecycleOwner (Activity/Fragment) deve chamar removeObserver() ou usar observe(viewLifecycleOwner). No iOS — removeObserver no deinit ou disposeBag no Combine.

PlataformaMecanismo ObserverCancelamento automáticoCancelamento manual
iOSNotificationCenterNãoremoveObserver() no deinit
iOSCombine (sink)Nãostore(in: &bag) — DisposeBag
iOSKVONãoremoveObserver() no deinit
AndroidLiveDataSim (LifecycleOwner)removeObserver() opcional
AndroidStateFlowAtravés de viewModelScopecancel() Job ao cancelar
AndroidRxJavaNãodispose() no CompositeDisposable

Referência fraca em subscritores — ao usar closures em blocos de subscrição, use [weak self] em Swift e referencie o âmbito do ciclo de vida em Kotlin. LiveData gere automaticamente as subscrições através de LifecycleOwner — a subscrição está ativa apenas quando o Lifecycle está no estado STARTED ou RESUMED. StateFlow no Compose usa collectAsState() com consciência do ciclo de vida. NotificationCenter no iOS requer [weak self] explícito porque o closure mantém uma referência forte a self.

Observer vs Publicador-Subscritor: qual a diferença

Observer (GoF) e Publicador-Subscritor (PubSub) — padrões semelhantes mas diferentes. No Observer, o publicador notifica diretamente os subscritores chamando os seus métodos. O publicador conhece os subscritores (armazena uma lista). No PubSub, o publicador e o subscritor não se conhecem — um mediador (Event Bus, Message Queue, NotificationCenter) está entre eles. O publicador envia uma mensagem para um canal, o subscritor ouve o canal. PubSub tem um acoplamento mais fraco.

Exemplos de PubSub no desenvolvimento móvel — NotificationCenter no iOS pode ser considerado PubSub: o publicador não conhece os subscritores — ele simplesmente publica uma notificação. EventBus ou Otto no Android (obsoletos). SharedFlow com BroadcastChannel — PubSub no mundo Kotlin. Em sistemas distribuídos, PubSub é implementado através de RabbitMQ, Kafka, Google PubSub. Para desenvolvimento móvel, PubSub é útil em arquitetura modular onde os módulos não devem depender uns dos outros.

O que escolher — para atualizações de UI (ViewModel → View), use Observer (LiveData, StateFlow, @Published). Para eventos entre módulos (autenticação, saída de sessão, mudança de tema) — PubSub (SharedFlow, NotificationCenter, EventBus). Observer é mais simples e eficiente dentro de um único ecrã, PubSub é mais flexível para eventos globais mas mais difícil de depurar devido a dependências implícitas.

Perguntas frequentes

Qual a diferença entre StateFlow e LiveData?

StateFlow é um tipo independente de plataforma do Kotlin Coroutines, enquanto LiveData está ligado ao ciclo de vida do Android. StateFlow suporta corrotinas e operadores, e pode ser testado sem Android. LiveData gere automaticamente as subscrições através de LifecycleOwner. A Google recomenda StateFlow para novos projetos em Kotlin + Compose, e LiveData para compatibilidade com Java.

Como evitar fugas de memória com NotificationCenter?

Use [weak self] no closure do manipulador e chame removeObserver() no deinit. Armazene uma referência ao observer (NSObjectProtocol) e remova-a quando o objeto for destruído. No Combine, use AnyCancellable e store(in:) para cancelamento automático ao libertar o DisposeBag.

Pode usar-se Observer no SwiftUI sem Combine?

Sim, o SwiftUI suporta ObservableObject com @Published e @StateObject/@ObservedObject — esta é uma implementação integrada do Observer. @Published notifica automaticamente a View sobre alterações. Combine não é necessário: ObservableObject usa o Publisher objectWillChange incorporado no SwiftUI. Combine adiciona operadores para transformação de fluxos.

Quando usar SharedFlow em vez de StateFlow?

SharedFlow — para eventos únicos (navegação, toasts, Snackbar) onde não é necessário um valor atual. StateFlow — para estado da UI (lista de dados, progresso de carregamento) onde é necessária uma imagem instantânea atual. SharedFlow não tem propriedade value e não devolve o último valor a novos subscritores.

Qual a diferença entre KVO e Combine no iOS?

KVO é um mecanismo legado do ObjC, requer @objc dynamic e funciona apenas com classes herdadas de NSObject. Combine é um framework moderno do Swift, type-safe, com operadores e integração com SwiftUI. Combine substitui KVO e NotificationCenter. A Apple recomenda Combine para novos projetos, KVO apenas para suporte legado.

Resumo

  • Observer — um padrão comportamental para notificar subscritores sobre alterações
  • iOS NotificationCenter — implementação PubSub com notificações nomeadas
  • iOS Combine — um framework reativo com Publisher e Subscriber
  • Android LiveData — Observer com consciência do ciclo de vida do Android Architecture Components
  • Android StateFlow — um contentor de estado reativo para Compose e corrotinas
  • Gestão de memória — cancelamento obrigatório de subscrição para prevenir fugas
  • Observer vs PubSub — subscrição direta vs mediador para acoplamento fraco

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.

Discutir o projeto

Leia também