Delegate é um padrão de design onde um objeto delega a execução de uma tarefa a outro objeto. No iOS, o padrão é implementado através de protocolos Swift e @protocol Objective-C. Delegação é um dos padrões fundamentais do Cocoa Touch, usado em UITableViewDelegate, UITextFieldDelegate e centenas de outras APIs da Apple. De acordo com a Apple Developer Documentation (2025), cerca de 70% das classes do sistema UIKit usam delegados para personalização de comportamento sem herança.
Principais pontos
Delegate é um padrão de design comportamental que permite a um objeto delegar parte de suas responsabilidades a outro objeto. Ao contrário da herança, onde uma classe filha sobrescreve os métodos da classe pai, o delegado usa composição: o objeto proprietário mantém uma referência ao delegado e chama seus métodos em pontos específicos.
O delegado define um protocolo — um conjunto de métodos que o delegado pode implementar. Os métodos são divididos em obrigatórios (required) e opcionais (optional). No Swift, os métodos opcionais do protocolo são marcados com a palavra-chave @objc optional.
O objeto A (proprietário) contém uma propriedade delegate — uma referência fraca ao objeto B (delegado). Quando um evento ocorre, A verifica se B implementa o método correspondente do protocolo e o chama. A referência fraca é obrigatória: sem ela, o delegado não pode ser liberado da memória porque o proprietário o mantém com uma referência forte.
protocol LoaderDelegate: AnyObject {
func loaderDidStart(_ loader: DataLoader)
func loader(_ loader: DataLoader, didLoad data: Data)
func loader(_ loader: DataLoader, didFailWith error: Error)
}
class DataLoader {
weak var delegate: LoaderDelegate?
func start() {
delegate?.loaderDidStart(self)
// carregamento assíncrono
}
}A classe DataLoader define o protocolo LoaderDelegate e chama os métodos do delegado em pontos-chave do ciclo de carregamento. AnyObject garante que o protocolo só pode ser implementado por classes — isso é necessário para a referência fraca.
A implementação do delegado no Swift inclui três etapas: declarar o protocolo, criar uma propriedade delegate fraca no proprietário e implementar o protocolo na classe delegada. Vamos ver um exemplo de um UITextField personalizado com validação.
protocol ValidatorDelegate: AnyObject {
func validate(_ input: String) -> Bool
func validatorDidFail(_ input: String)
}
class ValidatedTextField: UITextField {
weak var validator: ValidatorDelegate?
override func textDidChange() {
guard let text = self.text else { return }
if validator?.validate(text) == false {
validator?.validatorDidFail(text)
self.layer.borderColor = UIColor.red.cgColor
}
}
}
class LoginViewController: UIViewController, ValidatorDelegate {
let textField = ValidatedTextField()
override func viewDidLoad() {
super.viewDidLoad()
textField.validator = self
}
func validate(_ input: String) -> Bool {
return input.count >= 6
}
func validatorDidFail(_ input: String) {
print("Validação falhou: entrada muito curta")
}
}LoginViewController implementa o protocolo ValidatorDelegate e se define como delegado do textField. A cada alteração de texto, ValidatedTextField chama validate(_:), e se a validação falhar — validatorDidFail(_:). O controlador atua como intermediário entre a View e a lógica de validação.
A escolha entre delegado, notificações e closures depende do número de destinatários e do acoplamento dos componentes. Cada mecanismo resolve o problema de comunicação entre objetos, mas com diferentes trade-offs.
| Característica | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| Tipo de comunicação | 1:1 | 1:N | 1:1 |
| Acoplamento | Fraco (através de protocolo) | Muito fraco (chave de string) | Médio (captura de contexto) |
| Segurança de tipos | Completa | Nenhuma (Any?) | Completa |
| Risco de ciclo de retenção | Não (weak) | Não | Sim (captura de self) |
| Quando usar | Callbacks complexos com múltiplos métodos | Eventos que interessam a muitos | Closures simples com 1-2 callbacks |
Delegate é ideal quando você precisa passar uma série de eventos relacionados a um único destinatário. NotificationCenter é melhor para notificações de broadcast. Closure é para operações assíncronas simples, como completion handlers no URLSession.
Objective-C usa @protocol e @optional para declarar delegados. Ao contrário do Swift, todos os métodos do protocolo são opcionais por padrão. A principal diferença é a chamada respondsToSelector: antes de enviar uma mensagem ao delegado, pois o método pode não estar implementado.
@protocol ImageCacheDelegate
@optional
- (void)cacheDidStartDownload: (ImageCache *)cache;
- (void)cache: (ImageCache *)cache didCacheImage: (UIImage *)image;
@required
- (void)cache: (ImageCache *)cache didFailWithError: (NSError *)error;
@end
@interface ImageCache : NSObject
@property (nonatomic, weak) id<ImageCacheDelegate> delegate;
- (void)downloadImageAtURL: (NSURL *)url;
@end
@implementation ImageCache
- (void)downloadImageAtURL: (NSURL *)url {
if ([self.delegate respondsToSelector:@selector(cacheDidStartDownload:)]) {
[self.delegate cacheDidStartDownload:self];
}
// carregamento assíncrono de imagem
}
@endA principal diferença no Objective-C: antes de chamar um método opcional, é necessária uma verificação respondsToSelector:. No Swift, os métodos opcionais do protocolo eliminam essa verificação — o optional chaining (?.) lida automaticamente com a ausência de implementação.
Erros no uso de delegados levam a vazamentos de memória, travamentos do aplicativo e bugs não óbvios. Vamos ver os cinco problemas mais comuns.
Ciclo de retenção é o erro mais comum. Se a propriedade delegate for declarada como strong, e o delegado por sua vez possuir o objeto proprietário, um ciclo de retenção é formado. Ambos os objetos nunca serão liberados da memória. Solução: sempre declare o delegado como weak var no Swift ou @property (weak) no Objective-C.
Se o objeto proprietário sobreviver ao delegado e a referência permanecer, chamar o método do delegado resultará em EXC_BAD_ACCESS. A referência fraca resolve esse problema automaticamente: após o delegado ser liberado, a propriedade se torna nil. No entanto, em cenários multithread, você deve verificar adicionalmente o delegado na thread principal.
Um protocolo com mais de 20 métodos viola o Princípio de Segregação de Interfaces (ISP). UITableViewDelegate contém cerca de 30 métodos opcionais — esta é uma exceção histórica. Em seus próprios protocolos, é melhor dividir a responsabilidade em vários protocolos menores, cada um com seu próprio papel.
As APIs do sistema Apple usam ativamente o padrão Delegate. Vamos ver três exemplos-chave do UIKit que aparecem em todo aplicativo iOS.
| API | Protocolo | Métodos-chave |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
Cada um desses protocolos implementa diferentes aspectos do comportamento: UITableViewDelegate gerencia a aparência e a resposta ao toque, UITextFieldDelegate controla a entrada de texto, URLSessionDelegate lida com eventos de rede. Isso demonstra a flexibilidade do padrão: o delegado pode ser adaptado a qualquer área de responsabilidade.
Perguntas frequentes
Delegate gerencia o comportamento e a aparência (altura da célula, resposta ao toque). DataSource fornece dados (número de linhas, conteúdo das células). No UITableViewDelegate e UITableViewDataSource — são dois protocolos separados que dividem a responsabilidade entre apresentação e dados.
A referência fraca previne ciclos de retenção. O proprietário (por exemplo, UITableView) mantém uma referência ao delegado apenas como weak. Se o delegado (UIViewController) possuir a tabela, uma referência forte ao delegado criaria um ciclo: ViewController → UITableView → Delegate (ViewController). Weak quebra esse ciclo.
No SwiftUI, o padrão Delegate é usado com menos frequência — é substituído por @Binding, @State e closures. No entanto, o delegado ainda é usado para integração com UIKit através de UIViewRepresentable. Por exemplo, MKMapViewDelegate e WKUIDelegate continuam relevantes ao encapsular componentes UIKit no SwiftUI.
@objc optional permite declarar métodos opcionais em um protocolo Swift. Este é um mecanismo de compatibilidade com o runtime do Objective-C. Sem @objc, todos os métodos do protocolo Swift são obrigatórios por padrão. Optional é usado em protocolos UIKit onde o delegado pode implementar apenas os métodos que precisa.
Um objeto pode ter apenas um delegado para cada propriedade delegate. Se você precisar notificar vários objetos, use multicast delegate, um array de delegados ou NotificationCenter. O padrão Delegate foi originalmente projetado como uma relação 1:1.
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