Delegate: cos'è, pattern di delegazione e protocolli iOS

Autore: IT Sectr Pubblicato: 2026-02-17 Tempo di lettura: 9 min

Delegate è un pattern di progettazione in cui un oggetto delega l'esecuzione di un compito a un altro oggetto. In iOS, il pattern è implementato tramite i protocolli Swift e @protocol Objective-C. La delegazione è uno dei pattern fondamentali di Cocoa Touch, utilizzato in UITableViewDelegate, UITextFieldDelegate e centinaia di altre API Apple. Secondo Apple Developer Documentation (2025), circa il 70% delle classi di sistema UIKit utilizza delegati per personalizzare il comportamento senza ereditarietà.

Punti chiave

  • Delegate — un pattern in cui l'oggetto A delega l'esecuzione di un compito all'oggetto B tramite un protocollo o un'interfaccia
  • Protocollo Swift definisce un insieme di metodi che il delegato può o deve implementare
  • Riferimento debole al delegato è obbligatorio per prevenire cicli di ritenzione e perdite di memoria
  • UITableViewDelegate — l'esempio più comune di delegato in iOS con oltre 20 metodi opzionali
  • Differenza da Observer: il delegato è una relazione 1:1, mentre le notifiche funzionano secondo il principio 1:N

Cos'è il pattern Delegate?

Delegate è un pattern di progettazione comportamentale che permette a un oggetto di delegare parte delle sue responsabilità a un altro oggetto. A differenza dell'ereditarietà, dove una classe figlia sovrascrive i metodi del genitore, il delegato utilizza la composizione: l'oggetto proprietario mantiene un riferimento al delegato e chiama i suoi metodi in punti specifici.

Il delegato definisce un protocollo — un insieme di metodi che il delegato può implementare. I metodi sono divisi in obbligatori (required) e opzionali (optional). In Swift, i metodi opzionali del protocollo sono contrassegnati con la parola chiave @objc optional.

Come funziona il delegato

L'oggetto A (proprietario) contiene una proprietà delegate — un riferimento debole all'oggetto B (delegato). Quando si verifica un evento, A controlla se B implementa il metodo del protocollo corrispondente e lo chiama. Il riferimento debole è obbligatorio: senza di esso, il delegato non può essere rilasciato dalla memoria perché il proprietario lo trattiene con un riferimento forte.

Swift
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)
        // caricamento asincrono
    }
}

La classe DataLoader definisce il protocollo LoaderDelegate e chiama i metodi del delegato nei punti chiave del ciclo di caricamento. AnyObject garantisce che il protocollo possa essere implementato solo da classi — questo è necessario per il riferimento debole.

Come funziona Delegate in Swift?

L'implementazione del delegato in Swift include tre passaggi: dichiarazione del protocollo, creazione di una proprietà delegate debole nel proprietario e implementazione del protocollo nella classe delegata. Consideriamo un esempio di UITextField personalizzato con validazione.

Swift
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("Convalida fallita: input troppo breve")
    }
}

LoginViewController implementa il protocollo ValidatorDelegate e si assegna come delegato di textField. Ad ogni modifica del testo, ValidatedTextField chiama validate(_:), e se la validazione fallisce — validatorDidFail(_:). Il controller funge da intermediario tra la Vista e la logica di validazione.

Delegate vs NotificationCenter vs Closure

La scelta tra delegato, notifiche e closures dipende dal numero di destinatari e dall'accoppiamento dei componenti. Ogni meccanismo risolve il problema della comunicazione tra oggetti, ma con diversi compromessi.

CaratteristicaDelegateNotificationCenterClosure
Tipo di comunicazione1:11:N1:1
AccoppiamentoDebole (tramite protocollo)Molto debole (chiave stringa)Medio (cattura del contesto)
Sicurezza dei tipiCompletaNessuna (Any?)Completa
Rischio retain cycleNo (weak)NoSì (cattura di self)
Quando usareCallback complessi con più metodiEventi che interessano moltiClosure semplici con 1-2 callback

Delegate è ottimale quando è necessario passare una serie di eventi correlati a un singolo destinatario. NotificationCenter è migliore per notifiche broadcast. Closure è per operazioni asincrone semplici, come i completion handler in URLSession.

Implementazione di Delegate in Objective-C

Objective-C utilizza @protocol e @optional per dichiarare i delegati. A differenza di Swift, tutti i metodi del protocollo sono opzionali per impostazione predefinita. La differenza principale è la chiamata respondsToSelector: prima di inviare un messaggio al delegato, poiché il metodo potrebbe non essere implementato.

Objective-C
@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];
    }
    // caricamento asincrono dell'immagine
}
@end

La differenza principale in Objective-C: prima di chiamare un metodo opzionale, è necessario un controllo respondsToSelector:. In Swift, i metodi opzionali del protocollo eliminano questo controllo — l'optional chaining (?.) gestisce automaticamente l'assenza di implementazione.

Errori comuni nell'uso dei delegati

Gli errori nell'uso dei delegati portano a perdite di memoria, crash dell'applicazione e bug non ovvi. Esaminiamo i cinque problemi più comuni.

Riferimento forte al delegato

Il ciclo di ritenzione è l'errore più comune. Se la proprietà delegate è dichiarata come strong e il delegato a sua volta possiede l'oggetto proprietario, si forma un ciclo di ritenzione. Entrambi gli oggetti non verranno mai rilasciati dalla memoria. Soluzione: dichiarare sempre il delegato come weak var in Swift o @property (weak) in Objective-C.

Delegato non azzerato dopo dealloc

Se l'oggetto proprietario sopravvive al delegato e il riferimento rimane, chiamare il metodo del delegato provocherà EXC_BAD_ACCESS. Il riferimento debole risolve automaticamente questo problema: dopo il rilascio del delegato, la proprietà diventa nil. Tuttavia, in scenari multithread, è necessario verificare ulteriormente il delegato nel thread principale.

Numero eccessivo di metodi nel protocollo

Un protocollo con oltre 20 metodi viola il Principio di Segregazione delle Interfacce (ISP). UITableViewDelegate contiene circa 30 metodi opzionali — questa è un'eccezione storica. Nei tuoi protocolli, è meglio suddividere la responsabilità in più protocolli più piccoli, ciascuno con il proprio ruolo.

Esempi di Delegate nelle API di sistema iOS

Le API di sistema Apple utilizzano attivamente il pattern Delegate. Esaminiamo tre esempi chiave di UIKit che appaiono in ogni applicazione iOS.

APIProtocolloMetodi chiave
UITableViewUITableViewDelegatedidSelectRowAt, heightForRowAt, willDisplay
UITextFieldUITextFieldDelegateshouldChangeCharactersIn, didBeginEditing, shouldReturn
URLSessionURLSessionDelegatedidReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError

Ciascuno di questi protocolli implementa diversi aspetti del comportamento: UITableViewDelegate gestisce l'aspetto e la risposta al tocco, UITextFieldDelegate controlla l'input di testo, URLSessionDelegate gestisce gli eventi di rete. Questo dimostra la flessibilità del pattern: il delegato può essere adattato a qualsiasi area di responsabilità.

Domande frequenti

Qual è la differenza tra delegate e dataSource in iOS?

Delegate gestisce il comportamento e l'aspetto (altezza della cella, risposta al tocco). DataSource fornisce i dati (numero di righe, contenuto delle celle). In UITableViewDelegate e UITableViewDataSource — sono due protocolli separati che dividono la responsabilità tra presentazione e dati.

Perché delegate è dichiarato come weak?

Il riferimento debole previene i cicli di ritenzione. Il proprietario (ad esempio, UITableView) mantiene un riferimento al delegato solo come weak. Se il delegato (UIViewController) possiede la tabella, un riferimento forte al delegato creerebbe un ciclo: ViewController → UITableView → Delegate (ViewController). Weak rompe questo ciclo.

Si può usare delegate in SwiftUI?

In SwiftUI, il pattern Delegate è usato meno frequentemente — viene sostituito da @Binding, @State e closures. Tuttavia, il delegato è ancora utilizzato per l'integrazione UIKit tramite UIViewRepresentable. Ad esempio, MKMapViewDelegate e WKUIDelegate rimangono rilevanti quando si incapsulano componenti UIKit in SwiftUI.

Cos'è @objc optional nei protocolli Swift?

@objc optional permette di dichiarare metodi opzionali in un protocollo Swift. Questo è un meccanismo di compatibilità con il runtime di Objective-C. Senza @objc, tutti i metodi del protocollo Swift sono obbligatori per impostazione predefinita. Optional viene utilizzato nei protocolli UIKit dove il delegato può implementare solo i metodi di cui ha bisogno.

Quanti delegati può avere un oggetto?

Un oggetto può avere un solo delegato per ogni proprietà delegate. Se è necessario notificare più oggetti, utilizza un delegato multicast, un array di delegati o NotificationCenter. Il pattern Delegate è stato originariamente progettato come una relazione 1:1.

Riepilogo

  • Delegate — un pattern di progettazione comportamentale che trasferisce l'esecuzione di un compito da un oggetto a un altro tramite un protocollo
  • Protocollo Swift dichiara un insieme di metodi; i metodi opzionali sono contrassegnati con @objc optional, i metodi obbligatori sono sempre implementati
  • Riferimento debole al delegato è obbligatorio per prevenire cicli di ritenzione e perdite di memoria
  • UITableViewDelegate è l'esempio più noto; i suoi metodi gestiscono l'altezza, la selezione e la visualizzazione delle celle
  • Delegate differisce da NotificationCenter per una relazione 1:1 versus 1:N e la completa sicurezza dei tipi
  • Objective-C richiede il controllo respondsToSelector: per i metodi opzionali, Swift gestisce automaticamente
  • Raccomandazione: usa delegate per callback complessi con più metodi correlati e closure per closure semplici

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche