Delegate ist ein Entwurfsmuster, bei dem ein Objekt die Ausführung von Aufgaben an ein anderes Objekt über ein Protokoll mit vordefinierten Methoden delegiert. In der iOS-Entwicklung ist Delegate eines der grundlegenden Cocoa Touch-Muster, das für asynchrone Benachrichtigungen ohne direkte Kopplung zwischen Sender und Empfänger verwendet wird. Laut Apple Documentation (2025) wird Delegation in Foundation und UIKit zur Behandlung von Tabellenereignissen, Netzwerkanfragen und Standortverwaltung eingesetzt. Das Muster gewährleistet lose Kopplung von Komponenten und Wiederverwendung von Code.
Wichtige Erkenntnisse
Delegate (Delegat) ist ein Objekt, das ein bestimmtes Protokoll implementiert und Benachrichtigungen über Ereignisse eines anderen Objekts erhält. Das Delegationsmuster ist eine Alternative zur Vererbung: Anstatt eine Unterklasse zum Überschreiben von Methoden zu erstellen, delegiert ein Objekt die Ereignisbehandlung an ein externes Objekt. In iOS wird Delegation durch Swift-Protokolle mit erforderlichen und optionalen Methoden implementiert. Die delegate-Eigenschaft wird immer als weak var deklariert, um zirkuläre Referenzen zwischen Objekten zu vermeiden.
Ein Delegate-Protokoll definiert den Interaktionsvertrag zwischen Objekten. Erforderliche Methoden müssen vom Delegaten implementiert werden, sonst wird der Code nicht kompiliert. Optionale Methoden werden mit dem Attribut @objc optional markiert und erlauben dem Delegaten, nur auf relevante Ereignisse zu reagieren. Methodennamen folgen einer Konvention: Der erste Parameter ist das Senderobjekt, der zweite die Ereignisdaten. Beispielsweise zeigt tableView(_:didSelectRowAt:) an, dass der Sender UITableView und die Daten der Index der ausgewählten Zeile sind.
// Delegate-Protokoll
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)
}
// Klasse, die Delegate verwendet
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()
}
}
Die delegate-Eigenschaft muss als weak var deklariert werden, um retain cycles zu verhindern. Wäre die Referenz stark, würden der Delegat und das delegierende Objekt einander festhalten, und ARC könnte ihren Speicher nicht freigeben. Delegate-Protokolle erben von AnyObject (nur Klassen), was die Verwendung von weak ermöglicht. Strukturen und Aufzählungen können aufgrund der Wertsemantik keine Delegaten sein. Eine Alternative für Werttypen sind Callback-Closures.
class ViewController: DownloadManagerDelegate {
let manager = DownloadManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self // weak — kein retain cycle
manager.startDownload(from: url)
}
func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
processData(data)
}
func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
showError(error)
}
}
Das Delegate-Muster funktioniert eins-zu-eins: Ein Senderobjekt kann zu einem bestimmten Zeitpunkt nur einen Delegaten haben. Wenn ein Ereignis eintritt, überprüft der Sender, ob der Delegat gesetzt ist, und ruft die entsprechende Protokollmethode auf. Der Vorteil gegenüber direkten Aufrufen ist, dass der Sender den Typ des Delegaten nicht kennt, sondern nur, dass er dem Protokoll entspricht. Dies entspricht dem Abhängigkeitsinversionsprinzip (DIP) aus SOLID.
Der Delegat wird durch Zuweisung festgelegt: someObject.delegate = self. Wenn der Delegat freigegeben wird, wird die Eigenschaft aufgrund der weak-Semantik automatisch nil. Vor dem Aufruf einer Delegatenmethode wird der Delegat über optionale Verkettung geprüft: delegate?.method(). Ist der Delegat nil, wird der Aufruf ohne Absturz ignoriert. Für optionale Protokollmethoden wird eine zusätzliche Prüfung verwendet: delegate?.responds(to: #selector(...)), obwohl diese Prüfung in Swift normalerweise durch die optionale Methodendeklaration implizit ist.
In einer Multithread-Umgebung wird der Delegat zur asynchronen Rückgabe von Ergebnissen verwendet. URLSession stellt URLSessionDelegate mit Methoden bereit, die bei Empfang von Daten, Timeout oder Authentifizierungsfehler aufgerufen werden. Delegatenmethoden werden in der Hintergrundwarteschlange von URLSession ausgeführt, daher ist eine Dispatch an die Hauptwarteschlange für UI-Updates erforderlich. Der asynchrone Delegat blockiert den aufrufenden Thread nicht und ermöglicht die Fortsetzung anderer Aufgaben.
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 und Callback lösen dasselbe Problem — asynchrone Benachrichtigung — aber auf unterschiedliche Weise. Delegate verwendet ein Protokoll mit benannten Methoden, Callback verwendet einen Closure mit Kontexterfassung. Die Wahl hängt von der Anzahl der Ereignisse, der Komplexität der Signaturen und den architektonischen Präferenzen ab. Apple empfiehlt Delegate für APIs mit mehreren Ereignissen (UITableView — 20+ Methoden) und Callback für einmalige Abschlüsse.
Delegate ist vorzuziehen, wenn mehrere verschiedene Ereignisse von einer einzigen Quelle behandelt werden. Beispielsweise benachrichtigt CLLocationManager seinen Delegaten über Standortänderungen, Berechtigungsfehler, Geo-Fence-Ein/-Austritte und Statusänderungen des Dienstes. Jedes Ereignis ist eine separate Protokollmethode mit einem klaren Namen und typisierten Parametern. Delegate ist auch für die Verhaltenskonfiguration praktisch (should-, will-, did-Methoden).
Callback ist einfacher für einmalige Anfragen mit einem einzigen Ergebnis. Completion handler in URLSession.dataTask benötigt eine Zeile an der Aufrufstelle gegenüber mindestens drei Protokollmethoden. Callback ist auch für funktionale Ketten (map, flatMap, async/await) natürlicher. Bei einer Verschachtelung von mehr als 2-3 Ebenen wird Callback jedoch zur Callback-Hölle, während Delegate immer flach bleibt.
Das iOS SDK enthält Dutzende integrierter Delegate-Protokolle für verschiedene Subsysteme. Jedes ist für ein bestimmtes Interaktionsszenario konzipiert. Laut Apple Documentation (2025) sind die am häufigsten verwendeten Delegaten UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate und UNUserNotificationCenterDelegate. Diese Protokolle enthalten 3 bis 30 Methoden mit unterschiedlichen Graden der Erforderlichkeit.
UITableViewDelegate verwaltet das Erscheinungsbild und Verhalten von Tabellenzellen. Es enthält Methoden zur Behandlung von Zeilenauswahl, Konfiguration der Zellenhöhe, benutzerdefinierten Header/Footer-Ansichten und Wischaktionen. Alle Protokollmethoden sind optional, sodass nur die benötigte Funktionalität implementiert werden kann. Ohne Delegaten arbeitet die Tabelle mit Standardeinstellungen. Historisch wurde der Delegat mit UITableViewDataSource kombiniert.
URLSessionDelegate bietet detaillierte Kontrolle über HTTP-Anfragen. Delegatenmethoden werden beim Empfang einer Serverantwort, bei Datenankunft oder Download-Abschluss aufgerufen. Die spezialisierten Unterprotokolle URLSessionTaskDelegate und URLSessionDataDelegate erweitern die Basisfunktionalität für bestimmte Aufgabentypen. Ein Delegat ist für die Unterstützung von Hintergrunddownloads, SSL-Zertifikaten und benutzerdefinierter Weiterleitungsbehandlung erforderlich.
| Delegate | Methoden | Zweck |
|---|---|---|
| UITableViewDelegate | 25 | Tabellenerscheinung und -interaktion |
| UITextFieldDelegate | 8 | Texteingabe und Tastaturbehandlung |
| CLLocationManagerDelegate | 12 | Standortaktualisierungen und Geofences |
| URLSessionDelegate | 6 | HTTP-Sitzungs- und Zertifikatsverwaltung |
| UNUserNotificationCenterDelegate | 4 | Push-Benachrichtigungsbehandlung im Vordergrund |
Die Speicherverwaltung ist ein kritischer Aspekt bei der Arbeit mit Delegate in iOS. ARC (Automatic Reference Counting) verwaltet den Speicher automatisch, aber nur bei korrekter Verwendung von weak/unowned-Referenzen. Ein Verstoß gegen die Regeln führt zu Speicherlecks oder vorzeitiger Freigabe. Ein als strong deklarierter Delegat erzeugt einen retain cycle, wenn der Delegatbesitzer auch eine Referenz auf das delegierende Objekt hält.
Ein retain cycle tritt auf, wenn Objekt A (Besitzer) sich selbst als Delegaten von Objekt B festlegt und B eine starke Referenz auf den Delegaten hält. Beispiel: ViewController erstellt URLSession, setzt sich selbst als Delegaten der Sitzung, aber URLSession hält standardmäßig eine starke Referenz auf den Delegaten, wenn delegateQueue nicht angegeben ist. Die Lösung besteht darin, immer die API-Dokumentation zum Referenztyp des Delegaten (weak oder strong) zu prüfen und den Delegaten in deinit explizit auf nil zu setzen.
class SafeViewController: UIViewController {
private var session: URLSession?
private var service: NetworkService?
override func viewDidLoad() {
super.viewDidLoad()
service = NetworkService()
service?.delegate = self
}
deinit {
// Delegate in deinit auf nil setzen — best practice
service?.delegate = nil
session?.invalidateAndCancel()
}
}
// URLSession mit weak delegate über 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
)
}
}
Vor dem Aufruf einer Delegatenmethode müssen Sie überprüfen, ob der Delegat existiert (nicht nil) und die aufgerufene Methode implementiert. Für erforderliche Protokollmethoden ist keine Prüfung erforderlich — der Compiler garantiert die Implementierung. Für optionale Methoden verwenden Sie respond(to:) oder optionale Verkettung. Wenn der Delegat freigegeben wird, wird die weak-Referenz automatisch nil, und der Delegatenaufruf wird ignoriert. Dies ist ein sicheres Verhalten, das keine zusätzliche Behandlung erfordert.
Entwickler machen bei der Arbeit mit dem Delegate-Muster oft Fehler, besonders in der Anfangsphase des iOS-Lernens. Die häufigsten sind: retain cycle durch strong delegate, Vergessen von delegate?.method(), falsche Protokollmethodensignaturen, Setzen des Delegaten nach dem Start einer Operation und Multithreading-Kollisionen. Sehen wir uns jeden Fehler und seine Vermeidung an.
Der kritischste Fehler ist die Deklaration der delegate-Eigenschaft als strong var statt weak var. Dies erzeugt einen retain cycle, bei dem weder der Delegat noch das delegierende Objekt freigegeben werden können. Folgen: Speicherlecks, App-Verlangsamung und versteckte Fehler. Lösung: Verwenden Sie immer weak var für delegate, und lassen Sie das Protokoll von AnyObject erben, um die Verwendung von Werttypen als Delegaten zu verhindern.
Wenn der Delegat nach dem Aufruf einer asynchronen Methode gesetzt wird, können die ersten Ereignisse verloren gehen. Beispiel: Der Aufruf von startDownload() vor der Zuweisung manager.delegate = self führt zum Verlust des Abschluss-Callbacks, wenn der Download synchron oder sehr schnell ausgeführt wird. Lösung: Setzen Sie den Delegaten vor dem Aufruf der asynchronen Methode und dokumentieren Sie die Initialisierungsreihenfolge in den Protokollkommentaren.
Häufig gestellte Fragen
Weak verhindert einen retain cycle zwischen dem Delegaten und dem delegierenden Objekt. Wäre die Referenz stark, würden die Objekte einander festhalten und ARC könnte sie nicht freigeben. Eine weak-Referenz wird automatisch nil, wenn der Delegat freigegeben wird. Dies ist eine Standardpraxis von Cocoa Touch seit dem Aufkommen von Objective-C und wird in Swift aus Gründen der Abwärtskompatibilität beibehalten.
Delegate behandelt Ereignisse und verwaltet das Verhalten (Zellenhöhe, Reaktion auf Berührungen). DataSource liefert Daten zur Anzeige (Anzahl der Zeilen, Zellen). Der Delegat beantwortet die Frage „wie?“, dataSource die Frage „was?“. In iOS werden beide über Protokolle implementiert, oft im selben Controller, sind aber konzeptionell getrennt.
Nein, wenn das Protokoll von AnyObject (Klassenprotokoll) erbt. Weak-Referenzen sind nur für Referenztypen (Klassen) verfügbar. Für Werttypen (struct, enum) verwenden Sie Callback-Closures oder eine separate Wrapper-Klasse. Wenn Sie das Protokoll kontrollieren, können Sie die Vererbung von AnyObject vermeiden, aber dann ist weak verboten — wählen Sie bewusst zwischen weak-Delegat und struct-Delegat.
responds(to:) ist eine Methode von NSObjectProtocol, die prüft, ob ein Objekt den angegebenen Selektor implementiert. Sie wird verwendet, um optionale @objc-Protokollmethoden vor dem Aufruf zu prüfen. Ohne diese Prüfung würde der Aufruf einer nicht implementierten optionalen Methode zu NSInvalidArgumentException führen. In Swift kann die Prüfung für Protokolle mit @objc optional durch optionales Binding implizit erfolgen.
Nein, Delegate ist ein Delegationsmuster, kein Singleton. Im Gegensatz zu einem Singleton kann ein Delegat zur Laufzeit ersetzt werden und existiert in einer einzigen Instanz für jedes delegierende Objekt. Ein Objekt kann Delegat für mehrere Sender sein. Singleton ist ein Erzeugungsmuster, das eine einzige Klasseninstanz garantiert, was nichts mit Delegation zu tun hat.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch