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 (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.
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.
// 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()
}
}
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.
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)
}
}
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.
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.
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.
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 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.
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).
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.
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 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 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.
| Delegate | Métodos | Propósito |
|---|---|---|
| UITableViewDelegate | 25 | Aparência e interação da tabela |
| UITextFieldDelegate | 8 | Manipulação de entrada de texto e teclado |
| CLLocationManagerDelegate | 12 | Atualizações de localização e geofences |
| URLSessionDelegate | 6 | Gerenciamento de sessão HTTP e certificados |
| UNUserNotificationCenterDelegate | 4 | Manipulação de notificações push em primeiro plano |
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.
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.
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
)
}
}
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.
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.
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.
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
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.
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.
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.
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.
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
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