Delegate : qu'est-ce que c'est, le modèle de délégation et comment il fonctionne sur iOS

Auteur : IT Sectr Publié le : 2026-03-17 Temps de lecture : 10 min

Delegate est un modèle de conception dans lequel un objet délègue l'exécution de tâches à un autre objet via un protocole avec des méthodes prédéfinies. Dans le développement iOS, Delegate est l'un des modèles fondamentaux de Cocoa Touch, utilisé pour la notification asynchrone sans couplage direct entre l'expéditeur et le destinataire. Selon Apple Documentation (2025), la délégation est utilisée dans Foundation et UIKit pour gérer les événements de tableaux, les requêtes réseau et la gestion de localisation. Le modèle garantit un couplage faible des composants et la réutilisation du code.

Points clés

  • Delegate est un modèle où un objet confie le traitement des événements à un autre objet via un protocole.
  • Protocol dans Swift définit un ensemble de méthodes qu'un delegate doit ou peut implémenter.
  • Weak reference est obligatoire pour la propriété delegate afin d'éviter les retain cycles.
  • @objc optional permet de rendre les méthodes du protocole optionnelles à implémenter.
  • URLSessionDelegate est un delegate asynchrone pour gérer les événements de requête réseau.

Qu'est-ce que Delegate ?

Delegate (délégué) est un objet qui implémente un protocole spécifique et reçoit des notifications sur les événements d'un autre objet. Le modèle de délégation est une alternative à l'héritage : au lieu de créer une sous-classe pour redéfinir des méthodes, un objet délègue le traitement des événements à un objet externe. Dans iOS, la délégation est implémentée via des protocoles Swift avec des méthodes obligatoires et optionnelles. La propriété delegate est toujours déclarée comme weak var pour éviter les références circulaires entre objets.

Définition du protocole Delegate

Un protocole delegate définit le contrat d'interaction entre les objets. Les méthodes obligatoires doivent être implémentées par le délégué, sinon le code ne compilera pas. Les méthodes optionnelles sont marquées avec l'attribut @objc optional et permettent au délégué de répondre uniquement aux événements pertinents. Les noms de méthodes suivent une convention : le premier paramètre est l'objet expéditeur, le second sont les données de l'événement. Par exemple, tableView(_:didSelectRowAt:) indique que l'expéditeur est UITableView et les données sont l'index de la ligne sélectionnée.

swift
// Protocole 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 utilisant 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()
    }
}

Référence faible au délégué

La propriété delegate doit être déclarée comme weak var pour éviter les retain cycles. Si la référence était forte, le délégué et l'objet délégant se maintiendraient mutuellement, et ARC ne pourrait pas libérer leur mémoire. Les protocoles delegate héritent de AnyObject (classes uniquement), ce qui permet d'utiliser weak. Les structures et énumérations ne peuvent pas être déléguées en raison de la sémantique de valeur. Une alternative pour les types de valeur est les closures de callback.

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

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

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

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

Comment fonctionne le modèle Delegate dans iOS ?

Le modèle Delegate fonctionne en un-à-un : un objet expéditeur ne peut avoir qu'un seul délégué à un moment donné. Lorsqu'un événement se produit, l'expéditeur vérifie si le délégué est défini et appelle la méthode correspondante du protocole. L'avantage par rapport aux appels directs est que l'expéditeur ne connaît pas le type du délégué, seulement qu'il est conforme au protocole. Cela respecte le principe d'inversion des dépendances (DIP) de SOLID.

Cycle de vie du délégué

Le délégué est assigné par affectation : someObject.delegate = self. Lorsque le délégué est désalloué, la propriété devient automatiquement nil en raison de la sémantique weak. Avant d'appeler une méthode du délégué, le délégué est vérifié via le chaînage optionnel : delegate?.method(). Si le délégué est nil, l'appel est ignoré sans crash. Pour les méthodes optionnelles du protocole, une vérification supplémentaire est utilisée : delegate?.responds(to: #selector(...)), bien que dans Swift cette vérification soit généralement implicite via la déclaration de méthode optionnelle.

Notification asynchrone via Delegate

Dans un environnement multithreadé, le délégué est utilisé pour le retour asynchrone de résultats. URLSession fournit URLSessionDelegate avec des méthodes appelées lors de la réception de données, d'un délai d'attente ou d'une erreur d'authentification. Les méthodes du délégué sont exécutées dans la file d'attente d'arrière-plan d'URLSession, donc un dispatch vers la file principale est nécessaire pour les mises à jour de l'interface utilisateur. Le délégué asynchrone ne bloque pas le thread d'appel, permettant à d'autres tâches de continuer.

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 : comparaison des approches

Delegate et Callback résolvent le même problème — la notification asynchrone — mais de différentes manières. Delegate utilise un protocole avec des méthodes nommées, Callback utilise une closure avec capture de contexte. Le choix dépend du nombre d'événements, de la complexité des signatures et des préférences architecturales. Apple recommande Delegate pour les API avec de multiples événements (UITableView — 20+ méthodes) et Callback pour les achèvements uniques.

Quand Delegate gagne

Delegate est préférable lorsqu'il s'agit de gérer plusieurs événements différents d'une seule source. Par exemple, CLLocationManager notifie son délégué des changements de localisation, des erreurs d'autorisation, de l'entrée/sortie de géorepérages et des changements d'état du service. Chaque événement est une méthode de protocole séparée avec un nom clair et des paramètres typés. Delegate est également pratique pour la configuration du comportement (méthodes should, will, did).

Quand Callback gagne

Callback est plus simple pour les requêtes uniques avec un seul résultat. Completion handler dans URLSession.dataTask occupe une ligne au point d'appel contre au moins trois méthodes de protocole. Callback est également plus naturel pour les chaînes fonctionnelles (map, flatMap, async/await). Cependant, avec un imbrication de plus de 2-3 niveaux, Callback devient Callback Hell, alors que Delegate reste toujours plat.

Delegates intégrés dans iOS SDK

Le SDK iOS contient des dizaines de protocoles delegate intégrés pour divers sous-systèmes. Chacun est conçu pour un scénario d'interaction spécifique. Selon Apple Documentation (2025), les délégués les plus utilisés sont UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate et UNUserNotificationCenterDelegate. Ces protocoles contiennent de 3 à 30 méthodes avec différents niveaux d'obligation.

UITableViewDelegate

UITableViewDelegate gère l'apparence et le comportement des cellules du tableau. Il contient des méthodes pour gérer la sélection de lignes, configurer la hauteur des cellules, les vues personnalisées d'en-tête/pied et les actions de balayage. Toutes les méthodes du protocole sont optionnelles, permettant d'implémenter uniquement la fonctionnalité nécessaire. Sans délégué, le tableau fonctionne avec les paramètres par défaut. Historiquement, le délégué était combiné avec UITableViewDataSource.

URLSessionDelegate

URLSessionDelegate fournit un contrôle détaillé sur les requêtes HTTP. Les méthodes du délégué sont appelées lors de la réception d'une réponse du serveur, de l'arrivée des données ou de l'achèvement du téléchargement. Les sous-protocoles spécialisés URLSessionTaskDelegate et URLSessionDataDelegate étendent les fonctionnalités de base pour des types de tâches spécifiques. Le délégué est nécessaire pour prendre en charge les téléchargements en arrière-plan, les certificats SSL et la gestion personnalisée des redirections.

DelegateMéthodesObjectif
UITableViewDelegate25Apparence et interaction du tableau
UITextFieldDelegate8Gestion de la saisie de texte et du clavier
CLLocationManagerDelegate12Mises à jour de localisation et géorepérages
URLSessionDelegate6Gestion de session HTTP et certificats
UNUserNotificationCenterDelegate4Gestion des notifications push au premier plan

Gestion de la mémoire avec Delegate

La gestion de la mémoire est un aspect critique du travail avec delegate dans iOS. ARC (Automatic Reference Counting) gère la mémoire automatiquement, mais seulement avec l'utilisation correcte des références weak/unowned. Violer les règles entraîne des fuites de mémoire ou une désallocation prématurée. Un délégué déclaré comme strong crée un retain cycle si le propriétaire du délégué détient également une référence à l'objet délégant.

Retain cycle via Delegate

Un retain cycle se produit lorsque l'objet A (propriétaire) se définit comme délégué de l'objet B, et B maintient une référence forte au délégué. Exemple : ViewController crée URLSession, se définit comme délégué de la session, mais URLSession par défaut maintient une référence forte au délégué si delegateQueue n'est pas spécifié. La solution est de toujours vérifier la documentation de l'API pour le type de référence du délégué (weak ou strong) et de définir explicitement le délégué à nil dans deinit.

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

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

    deinit {
        // Mettre le délégué à nil dans deinit — best practice
        service?.delegate = nil
        session?.invalidateAndCancel()
    }
}

// URLSession avec 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
        )
    }
}

Vérification sécurisée du délégué avant l'appel

Avant d'appeler une méthode du délégué, vous devez vérifier que le délégué existe (n'est pas nil) et implémente la méthode appelée. Pour les méthodes obligatoires du protocole, aucune vérification n'est nécessaire — le compilateur garantit l'implémentation. Pour les méthodes optionnelles, utilisez respond(to:) ou le chaînage optionnel. Si le délégué est désalloué, la référence weak devient automatiquement nil, et l'appel au délégué est ignoré. Ce comportement est sûr et ne nécessite aucun traitement supplémentaire.

Erreurs typiques lors de l'implémentation de Delegate

Les développeurs commettent souvent des erreurs lorsqu'ils travaillent avec le modèle Delegate, en particulier aux premiers stades de l'apprentissage d'iOS. Les plus courantes incluent : retain cycle dû à un delegate strong, oubli d'appeler delegate?.method(), signature incorrecte des méthodes du protocole, définition du délégué après le démarrage d'une opération et collisions de multithreading. Examinons chaque erreur et comment l'éviter.

Référence strong au lieu de weak

L'erreur la plus critique est de déclarer la propriété delegate comme strong var au lieu de weak var. Cela crée un retain cycle où ni le délégué ni l'objet délégant ne peuvent être libérés. Conséquences : fuites de mémoire, ralentissement de l'application et bugs cachés. Solution : utilisez toujours weak var pour le delegate et faites en sorte que le protocole hérite de AnyObject pour empêcher l'utilisation de types de valeur comme délégués.

Définir le délégué après le démarrage d'une opération

Si le délégué est défini après l'appel d'une méthode asynchrone, les premiers événements peuvent être perdus. Exemple : appeler startDownload() avant d'assigner manager.delegate = self entraîne la perte du callback d'achèvement si le téléchargement s'exécute de manière synchrone ou très rapidement. Solution : définissez le délégué avant d'appeler la méthode asynchrone et documentez l'ordre d'initialisation dans les commentaires du protocole.

Questions fréquentes

Pourquoi le delegate est-il déclaré comme weak ?

Weak empêche un retain cycle entre le délégué et l'objet délégant. Si la référence était forte, les objets se maintiendraient mutuellement et ARC ne pourrait pas les libérer. Une référence weak devient automatiquement nil lorsque le délégué est désalloué. C'est une pratique standard de Cocoa Touch depuis l'avènement d'Objective-C et elle est conservée dans Swift pour la rétrocompatibilité.

Quelle est la différence entre delegate et dataSource ?

Delegate gère les événements et le comportement (hauteur des cellules, réponse aux tap). DataSource fournit les données à afficher (nombre de lignes, cellules). Le délégué répond à la question « comment ? », dataSource répond à la question « quoi ? ». Dans iOS, les deux sont implémentés via des protocoles, souvent dans le même contrôleur, mais sont conceptuellement séparés.

Peut-on utiliser une struct comme délégué ?

Non, si le protocole hérite de AnyObject (protocole de classe). Les références weak sont disponibles uniquement pour les types référence (classes). Pour les types valeur (struct, enum), utilisez des closures de callback ou une classe wrapper séparée. Si vous contrôlez le protocole, vous pouvez éviter d'hériter de AnyObject, mais alors weak est interdit — choisissez consciemment entre un délégué weak et un délégué struct.

Qu'est-ce que la méthode responds(to:) et à quoi sert-elle ?

responds(to:) est une méthode de NSObjectProtocol qui vérifie si un objet implémente le sélecteur spécifié. Elle est utilisée pour vérifier les méthodes optionnelles @objc du protocole avant de les appeler. Sans cette vérification, appeler une méthode optionnelle non implémentée entraînerait NSInvalidArgumentException. Dans Swift, pour les protocoles avec @objc optional, la vérification peut être implicite via le binding optionnel.

Delegate est-il un singleton ou non ?

Non, delegate est un modèle de délégation, pas un singleton. Contrairement à un singleton, un délégué peut être remplacé à l'exécution et existe en une seule instance pour chaque objet délégant. Un objet peut être délégué pour plusieurs expéditeurs. Le singleton est un modèle de création qui garantit une instance unique de classe, ce qui n'a rien à voir avec la délégation.

Résumé

  • Delegate est un modèle où un objet délègue le traitement des événements à un autre objet via un protocole avec des méthodes typées.
  • Weak var est obligatoire pour la propriété delegate afin d'éviter les retain cycles et les fuites de mémoire.
  • Protocol définit les méthodes de délégation obligatoires et optionnelles (@objc optional).
  • iOS SDK contient 15+ protocoles delegate intégrés : UITableViewDelegate, URLSessionDelegate, CLLocationManagerDelegate.
  • Delegate est préférable à callback lorsqu'il y a 3+ événements différents d'une seule source (CLLocationManager).
  • Délégué asynchrone est utilisé dans URLSession pour recevoir des données et la progression sans bloquer le thread.
  • Définissez le délégué avant de lancer une opération asynchrone et mettez-le à nil dans deinit pour une gestion sécurisée de la mémoire.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi