Delegate es un patrón de diseño donde un objeto delega la ejecución de una tarea a otro objeto. En iOS, el patrón se implementa mediante protocolos Swift y @protocol Objective-C. La delegación es uno de los patrones fundamentales de Cocoa Touch, utilizado en UITableViewDelegate, UITextFieldDelegate y cientos de otras API de Apple. Según Apple Developer Documentation (2025), alrededor del 70% de las clases del sistema UIKit utilizan delegados para personalizar el comportamiento sin herencia.
Puntos clave
Delegate es un patrón de diseño de comportamiento que permite a un objeto delegar parte de sus responsabilidades a otro objeto. A diferencia de la herencia, donde una clase hija sobrescribe los métodos del padre, el delegado utiliza composición: el objeto propietario mantiene una referencia al delegado y llama a sus métodos en puntos específicos.
El delegado define un protocolo — un conjunto de métodos que el delegado puede implementar. Los métodos se dividen en obligatorios y opcionales. En Swift, los métodos opcionales del protocolo se marcan con la palabra clave @objc optional.
El objeto A (propietario) contiene una propiedad delegate — una referencia débil al objeto B (delegado). Cuando ocurre un evento, A verifica si B implementa el método correspondiente del protocolo y lo llama. La referencia débil es obligatoria: sin ella, el delegado no puede ser liberado de la memoria porque el propietario lo retiene con una referencia fuerte.
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)
// carga asíncrona
}
}La clase DataLoader define el protocolo LoaderDelegate y llama a los métodos del delegado en puntos clave del ciclo de carga. AnyObject garantiza que el protocolo solo puede ser implementado por clases — esto es necesario para la referencia débil.
La implementación del delegado en Swift incluye tres pasos: declarar el protocolo, crear una propiedad delegate débil en el propietario e implementar el protocolo en la clase delegada. Veamos un ejemplo de un UITextField personalizado con validación.
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("Validación fallida: entrada demasiado corta")
}
}LoginViewController implementa el protocolo ValidatorDelegate y se asigna como delegado de textField. Con cada cambio de texto, ValidatedTextField llama a validate(_:), y si la validación falla — validatorDidFail(_:). El controlador actúa como intermediario entre la Vista y la lógica de validación.
La elección entre delegado, notificaciones y closures depende del número de receptores y el acoplamiento de componentes. Cada mecanismo resuelve el problema de comunicación entre objetos, pero con diferentes compromisos.
| Característica | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| Tipo de comunicación | 1:1 | 1:N | 1:1 |
| Acoplamiento | Débil (mediante protocolo) | Muy débil (clave de cadena) | Medio (captura de contexto) |
| Seguridad de tipos | Completa | Ninguna (Any?) | Completa |
| Riesgo de ciclo de retención | No (weak) | No | Sí (captura de self) |
| Cuándo usar | Callbacks complejos con múltiples métodos | Eventos que interesan a muchos | Closures simples con 1-2 callbacks |
Delegate es óptimo cuando necesitas pasar una serie de eventos relacionados a un solo receptor. NotificationCenter es mejor para notificaciones de difusión. Closure es para operaciones asíncronas simples, como los completion handlers en URLSession.
Objective-C utiliza @protocol y @optional para declarar delegados. A diferencia de Swift, todos los métodos del protocolo son opcionales por defecto. La diferencia clave es la llamada respondsToSelector: antes de enviar un mensaje al delegado, ya que el método puede no 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];
}
// carga asíncrona de imagen
}
@endLa diferencia clave en Objective-C: antes de llamar a un método opcional, es necesaria una verificación de respondsToSelector:. En Swift, los métodos opcionales del protocolo eliminan esta verificación — el optional chaining (?.) maneja automáticamente la ausencia de implementación.
Los errores en el uso de delegados provocan fugas de memoria, caídas de la aplicación y errores no evidentes. Veamos los cinco problemas más comunes.
El ciclo de retención es el error más común. Si la propiedad delegate se declara como strong, y el delegado a su vez posee el objeto propietario, se forma un ciclo de retención. Ambos objetos nunca serán liberados de la memoria. Solución: declarar siempre el delegado como weak var en Swift o @property (weak) en Objective-C.
Si el objeto propietario sobrevive al delegado y la referencia permanece, llamar al método del delegado provocará EXC_BAD_ACCESS. La referencia débil resuelve este problema automáticamente: después de que el delegado se libera, la propiedad se vuelve nil. Sin embargo, en escenarios multiproceso, se debe verificar adicionalmente al delegado en el hilo principal.
Un protocolo con más de 20 métodos viola el Principio de Segregación de Interfaces (ISP). UITableViewDelegate contiene alrededor de 30 métodos opcionales — esta es una excepción histórica. En tus propios protocolos, es mejor dividir la responsabilidad en varios protocolos más pequeños, cada uno con su propio rol.
Las API del sistema Apple utilizan activamente el patrón Delegate. Veamos tres ejemplos clave de UIKit que aparecen en cada aplicación iOS.
| API | Protocolo | Métodos clave |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
Cada uno de estos protocolos implementa diferentes aspectos del comportamiento: UITableViewDelegate gestiona la apariencia y la respuesta al tacto, UITextFieldDelegate controla la entrada de texto, URLSessionDelegate maneja eventos de red. Esto demuestra la flexibilidad del patrón: el delegado puede adaptarse a cualquier área de responsabilidad.
Preguntas frecuentes
Delegate gestiona el comportamiento y la apariencia (altura de celda, respuesta al tacto). DataSource proporciona datos (número de filas, contenido de celdas). En UITableViewDelegate y UITableViewDataSource — son dos protocolos separados que dividen la responsabilidad entre presentación y datos.
La referencia débil previene los ciclos de retención. El propietario (por ejemplo, UITableView) mantiene una referencia al delegado solo como weak. Si el delegado (UIViewController) posee la tabla, una referencia fuerte al delegado crearía un ciclo: ViewController → UITableView → Delegate (ViewController). Weak rompe este ciclo.
En SwiftUI, el patrón Delegate se usa con menos frecuencia — se reemplaza por @Binding, @State y closures. Sin embargo, el delegado todavía se utiliza para la integración con UIKit a través de UIViewRepresentable. Por ejemplo, MKMapViewDelegate y WKUIDelegate siguen siendo relevantes al envolver componentes UIKit en SwiftUI.
@objc optional permite declarar métodos opcionales en un protocolo Swift. Este es un mecanismo de compatibilidad con el runtime de Objective-C. Sin @objc, todos los métodos del protocolo Swift son obligatorios por defecto. Optional se utiliza en protocolos UIKit donde el delegado puede implementar solo los métodos que necesita.
Un objeto puede tener solo un delegado para cada propiedad delegate. Si necesitas notificar a múltiples objetos, usa multicast delegate, un array de delegados o NotificationCenter. El patrón Delegate está diseñado originalmente como una relación 1:1.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también