Delegate: o que é, o padrão de delegação e como funciona no iOS

Autor: IT Sectr Publicado: 2026-03-17 Tempo de leitura: 10 min

Delegate é um padrão de design no qual um objeto delega a execução de tarefas a outro objeto através de um protocolo com métodos predefinidos. No desenvolvimento iOS, Delegate é um dos padrões fundamentais do Cocoa Touch, usado para notificação assíncrona sem acoplamento direto entre remetente e receptor. De acordo com a Apple Documentation (2025), a delegação é usada em Foundation e UIKit para manipular eventos de tabela, solicitações de rede e gerenciamento de localização. O padrão garante baixo acoplamento de componentes e reutilização de código.

Principais conclusões

  • Delegate é um padrão onde um objeto confia o tratamento de eventos a outro objeto através de um protocolo.
  • Protocol em Swift define um conjunto de métodos que um delegate deve ou pode implementar.
  • Weak reference é obrigatória para a propriedade delegate para evitar retain cycle.
  • @objc optional permite tornar métodos do protocolo opcionais para implementação.
  • URLSessionDelegate é um delegate assíncrono para lidar com eventos de solicitação de rede.

O que é Delegate?

Delegate (delegado) é um objeto que implementa um protocolo específico e recebe notificações sobre eventos de outro objeto. O padrão Delegation é uma alternativa à herança: em vez de criar uma subclasse para sobrescrever métodos, um objeto delega o tratamento de eventos a um objeto externo. No iOS, a delegação é implementada através de protocolos Swift com métodos obrigatórios e opcionais. A propriedade delegate é sempre declarada como weak var para evitar referências circulares entre objetos.

Definindo o protocolo Delegate

Um protocolo delegate define o contrato de interação entre objetos. Métodos obrigatórios devem ser implementados pelo delegado, caso contrário o código não compilará. Métodos opcionais são marcados com o atributo @objc optional e permitem que o delegado responda apenas a eventos relevantes. Os nomes dos métodos seguem uma convenção: o primeiro parâmetro é o objeto remetente, o segundo são os dados do evento. Por exemplo, tableView(_:didSelectRowAt:) indica que o remetente é UITableView e os dados são o índice da linha selecionada.

swift
// Protocolo Delegate
protocol DownloadManagerDelegate: AnyObject {
    func downloadManager(_ manager: DownloadManager,
                          didFinishWith data: Data)
    func downloadManager(_ manager: DownloadManager,
                          didFailWith error: Error)

    @objc optional func downloadManager(_ manager: DownloadManager,
                                didUpdateProgress progress: Float)
}

// Classe que usa Delegate
class DownloadManager {
    weak var delegate: DownloadManagerDelegate?

    func startDownload(from url: URL) {
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let self else { return }
            if let error = error {
                self.delegate?.downloadManager(self, didFailWith: error)
            } else if let data = data {
                self.delegate?.downloadManager(self, didFinishWith: data)
            }
        }.resume()
    }
}

Referência fraca ao delegate

A propriedade delegate deve ser declarada como weak var para evitar retain cycles. Se a referência fosse forte, o delegado e o objeto delegante se segurariam mutuamente, e o ARC não poderia liberar sua memória. Os protocolos delegate herdam AnyObject (apenas classes), o que permite usar weak. Estruturas e enumerações não podem ser delegados devido à semântica de valor. Uma alternativa para value types são as callback closures.

swift
class ViewController: DownloadManagerDelegate {
    let manager = DownloadManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self // weak — sem retain cycle
        manager.startDownload(from: url)
    }

    func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
        processData(data)
    }

    func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
        showError(error)
    }
}

Como funciona o padrão Delegate no iOS?

O padrão Delegate funciona um-para-um: um objeto remetente pode ter apenas um delegate em um determinado momento. Quando um evento ocorre, o remetente verifica se o delegate está definido e chama o método correspondente do protocolo. A vantagem sobre chamadas diretas é que o remetente não conhece o tipo do delegate, apenas que ele está em conformidade com o protocolo. Isso está de acordo com o Princípio da Inversão de Dependência (DIP) do SOLID.

Ciclo de vida do delegado

O delegado é atribuído através de atribuição: someObject.delegate = self. Quando o delegado é desalocado, a propriedade torna-se automaticamente nil devido à semântica weak. Antes de chamar um método do delegado, o delegate é verificado através de optional chaining: delegate?.method(). Se delegate é nil, a chamada é ignorada sem crash. Para métodos opcionais do protocolo, uma verificação adicional é usada: delegate?.responds(to: #selector(...)), embora em Swift essa verificação seja geralmente implícita através da declaração de método opcional.

Notificação assíncrona via Delegate

Em um ambiente multithread, o delegate é usado para retorno assíncrono de resultados. URLSession fornece URLSessionDelegate com métodos chamados quando os dados são recebidos, em timeout ou erro de autenticação. Os métodos do delegate são executados na fila em segundo plano do URLSession, portanto, é necessário despachar para a fila principal para atualizações de UI. O delegate assíncrono não bloqueia a thread de chamada, permitindo que outras tarefas continuem.

swift
class NetworkService: NSObject, URLSessionDataDelegate {
    private lazy var session = URLSession(
        configuration: .default,
        delegate: self,
        delegateQueue: OperationQueue()
    )
    private var receivedData = Data()

    func urlSession(_ session: URLSession,
                    dataTask: URLSessionDataTask,
                    didReceive data: Data) {
        receivedData.append(data)
        let progress = Float(receivedData.count) / Float(expectedSize)
        DispatchQueue.main.async {
            self.progressHandler?(progress)
        }
    }

    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    didCompleteWithError error: Error?) {
        if let error = error {
            delegate?.networkService(self, didFailWith: error)
        } else {
            delegate?.networkService(self, didReceive: receivedData)
        }
    }
}

Delegate vs Callback: comparação de abordagens

Delegate e Callback resolvem o mesmo problema — notificação assíncrona — mas de maneiras diferentes. Delegate usa um protocolo com métodos nomeados, callback usa uma closure com captura de contexto. A escolha depende do número de eventos, complexidade das assinaturas e preferências arquitetônicas. A Apple recomenda delegate para APIs com múltiplos eventos (UITableView — 20+ métodos) e callback para conclusões únicas.

Quando Delegate vence

Delegate é preferível ao lidar com vários eventos diferentes de uma única fonte. Por exemplo, CLLocationManager notifica seu delegado sobre mudanças de localização, erros de permissão, entrada/saída de geofences e alterações no status do serviço. Cada evento é um método de protocolo separado com um nome claro e parâmetros tipados. Delegate também é conveniente para configuração de comportamento (métodos should, will, did).

Quando Callback vence

Callback é mais simples para solicitações únicas com um único resultado. Completion handler em URLSession.dataTask ocupa uma linha no local da chamada contra pelo menos três métodos de protocolo. Callback também é mais natural para cadeias funcionais (map, flatMap, async/await). No entanto, com aninhamento além de 2-3 níveis, o callback se torna Callback Hell, enquanto o delegate sempre permanece plano.

Delegados integrados no iOS SDK

O iOS SDK contém dezenas de protocolos delegate integrados para vários subsistemas. Cada um é projetado para um cenário de interação específico. De acordo com a Apple Documentation (2025), os delegados mais usados são UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate e UNUserNotificationCenterDelegate. Esses protocolos contêm de 3 a 30 métodos com diferentes níveis de obrigatoriedade.

UITableViewDelegate

UITableViewDelegate gerencia a aparência e o comportamento das células da tabela. Ele contém métodos para lidar com seleção de linhas, configurar altura de células, visualizações personalizadas de header/footer e ações de deslizar. Todos os métodos do protocolo são opcionais, permitindo implementar apenas a funcionalidade necessária. Sem delegado, a tabela funciona com configurações padrão. Historicamente, o delegate era combinado com UITableViewDataSource.

URLSessionDelegate

URLSessionDelegate fornece controle detalhado sobre solicitações HTTP. Os métodos do delegado são chamados quando uma resposta do servidor é recebida, os dados chegam ou o download é concluído. Os subprotocolos especializados URLSessionTaskDelegate e URLSessionDataDelegate estendem a funcionalidade básica para tipos específicos de tarefas. O delegate é necessário para suportar downloads em segundo plano, certificados SSL e manipulação personalizada de redirecionamentos.

DelegateMétodosPropósito
UITableViewDelegate25Aparência e interação da tabela
UITextFieldDelegate8Manipulação de entrada de texto e teclado
CLLocationManagerDelegate12Atualizações de localização e geofences
URLSessionDelegate6Gerenciamento de sessão HTTP e certificados
UNUserNotificationCenterDelegate4Manipulação de notificações push em primeiro plano

Gerenciamento de memória com Delegate

O gerenciamento de memória é um aspecto crítico do trabalho com delegate no iOS. ARC (Automatic Reference Counting) gerencia a memória automaticamente, mas apenas com o uso correto de referências weak/unowned. Violar as regras leva a vazamentos de memória ou desalocação prematura. Um delegate declarado como strong cria um retain cycle se o proprietário do delegado também mantiver uma referência ao objeto delegante.

Retain cycle via Delegate

Um retain cycle ocorre quando o objeto A (proprietário) se define como delegate do objeto B, e B mantém uma referência forte ao delegate. Exemplo: ViewController cria URLSession, define-se como delegate da sessão, mas URLSession por padrão mantém uma referência forte ao delegate se delegateQueue não for especificado. A solução é sempre verificar a documentação da API quanto ao tipo de referência do delegate (weak ou strong) e definir explicitamente o delegate como nil em deinit.

swift
class SafeViewController: UIViewController {
    private var session: URLSession?
    private var service: NetworkService?

    override func viewDidLoad() {
        super.viewDidLoad()
        service = NetworkService()
        service?.delegate = self
    }

    deinit {
        // Definir delegate como nil em deinit — best practice
        service?.delegate = nil
        session?.invalidateAndCancel()
    }
}

// URLSession com weak delegate via NSObject
class WeakDelegateSession: NSObject {
    private weak var delegate: URLSessionDelegate?

    func createSession() -> URLSession {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        return URLSession(
            configuration: .default,
            delegate: self,
            delegateQueue: queue
        )
    }
}

Verificação segura do delegate antes de chamar

Antes de chamar um método do delegado, você deve verificar se o delegado existe (não é nil) e implementa o método chamado. Para métodos obrigatórios do protocolo, nenhuma verificação é necessária — o compilador garante a implementação. Para métodos opcionais, use respond(to:) ou optional chaining. Se o delegado for desalocado, a referência weak torna-se automaticamente nil, e a chamada do delegado é ignorada. Este é um comportamento seguro que não requer tratamento adicional.

Erros típicos na implementação de Delegate

Os desenvolvedores frequentemente cometem erros ao trabalhar com o padrão Delegate, especialmente nos estágios iniciais do aprendizado de iOS. Os mais comuns incluem: retain cycle devido a delegate strong, esquecer de chamar delegate?.method(), assinatura incorreta de métodos do protocolo, definir o delegate após iniciar uma operação e colisões de multithreading. Vamos examinar cada erro e como evitá-lo.

Referência strong em vez de weak

O erro mais crítico é declarar a propriedade delegate como strong var em vez de weak var. Isso cria um retain cycle onde nem o delegado nem o objeto delegante podem ser liberados. Consequências: vazamentos de memória, lentidão do aplicativo e bugs ocultos. Solução: sempre use weak var para delegate e faça o protocolo herdar de AnyObject para evitar o uso de value types como delegados.

Definir o delegate após iniciar uma operação

Se o delegate for definido após chamar um método assíncrono, os primeiros eventos podem ser perdidos. Exemplo: chamar startDownload() antes de atribuir manager.delegate = self resulta na perda do callback de conclusão se o download for executado sincronamente ou muito rapidamente. Solução: defina o delegate antes de chamar o método assíncrono e documente a ordem de inicialização nos comentários do protocolo.

Perguntas frequentes

Por que o delegate é declarado como weak?

Weak evita um retain cycle entre o delegado e o objeto delegante. Se a referência fosse forte, os objetos se segurariam mutuamente e o ARC não poderia liberá-los. Uma referência weak torna-se automaticamente nil quando o delegado é desalocado. Esta é uma prática padrão do Cocoa Touch desde o advento do Objective-C e é preservada em Swift para compatibilidade reversa.

Qual é a diferença entre delegate e dataSource?

Delegate lida com eventos e gerencia o comportamento (altura das células, resposta a toques). DataSource fornece dados para exibição (número de linhas, células). O delegado responde à pergunta “como?”, o dataSource responde à pergunta “o quê?”. No iOS, ambos são implementados através de protocolos, muitas vezes no mesmo controlador, mas estão conceitualmente separados.

Pode-se usar uma struct como delegate?

Não, se o protocolo herdar de AnyObject (protocolo de classe). Referências weak estão disponíveis apenas para reference types (classes). Para value types (struct, enum), use callback closures ou uma classe wrapper separada. Se você controla o protocolo, pode evitar herdar AnyObject, mas então weak é proibido — escolha conscientemente entre delegate weak e delegate struct.

O que é o método responds(to:) e para que serve?

responds(to:) é um método do NSObjectProtocol que verifica se um objeto implementa o seletor especificado. É usado para verificar métodos opcionais @objc do protocolo antes de chamá-los. Sem essa verificação, chamar um método opcional não implementado resultaria em NSInvalidArgumentException. Em Swift, para protocolos com @objc optional, a verificação pode ser implícita através de optional binding.

Delegate é um singleton ou não?

Não, delegate é um padrão de delegação, não um singleton. Ao contrário de um singleton, um delegado pode ser substituído em tempo de execução e existe em uma única instância para cada objeto delegante. Um objeto pode ser delegado de vários remetentes. Singleton é um padrão criacional que garante uma única instância de classe, o que não tem relação com delegação.

Resumo

  • Delegate é um padrão onde um objeto delega o tratamento de eventos a outro objeto através de um protocolo com métodos tipados.
  • Weak var é obrigatório para a propriedade delegate para evitar retain cycles e vazamentos de memória.
  • Protocol define métodos de delegação obrigatórios e opcionais (@objc optional).
  • iOS SDK contém 15+ protocolos delegate integrados: UITableViewDelegate, URLSessionDelegate, CLLocationManagerDelegate.
  • Delegate é preferível a callback quando há 3+ eventos diferentes de uma única fonte (CLLocationManager).
  • Delegate assíncrono é usado em URLSession para receber dados e progresso sem bloquear a thread.
  • Defina o delegate antes de iniciar uma operação assíncrona e defina-o como nil em deinit para gerenciamento seguro de memória.

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