Delegate ist ein Entwurfsmuster, bei dem ein Objekt die Ausführung einer Aufgabe an ein anderes Objekt delegiert. In iOS wird das Muster durch Swift-Protokolle und @protocol Objective-C implementiert. Delegation ist eines der grundlegenden Cocoa Touch-Muster, das in UITableViewDelegate, UITextFieldDelegate und Hunderten anderer Apple-APIs verwendet wird. Laut Apple Developer Documentation (2025) verwenden etwa 70% der UIKit-Systemklassen Delegaten zur Verhaltensanpassung ohne Vererbung.
Wichtigste Punkte
Delegate ist ein Verhaltensentwurfsmuster, das es einem Objekt ermöglicht, einen Teil seiner Verantwortlichkeiten an ein anderes Objekt zu delegieren. Im Gegensatz zur Vererbung, bei der eine Kindklasse Elternmethoden überschreibt, verwendet der Delegat Komposition: Das Besitzerobjekt hält eine Referenz auf den Delegaten und ruft seine Methoden an bestimmten Punkten auf.
Der Delegat definiert ein Protokoll — eine Reihe von Methoden, die der Delegat implementieren kann. Die Methoden werden in erforderliche (required) und optionale (optional) unterteilt. In Swift werden optionale Protokollmethoden mit dem Schlüsselwort @objc optional markiert.
Objekt A (Besitzer) enthält eine delegate-Eigenschaft — eine schwache Referenz auf Objekt B (Delegat). Wenn ein Ereignis eintritt, prüft A, ob B die entsprechende Protokollmethode implementiert, und ruft sie auf. Die schwache Referenz ist obligatorisch: Ohne sie kann der Delegat nicht aus dem Speicher freigegeben werden, da der Besitzer ihn mit einer starken Referenz hält.
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)
// asynchrones Laden
}
}Die Klasse DataLoader definiert das LoaderDelegate-Protokoll und ruft Delegatenmethoden an wichtigen Punkten des Ladeprozesses auf. AnyObject stellt sicher, dass das Protokoll nur von Klassen implementiert werden kann — dies ist für die schwache Referenz erforderlich.
Die Delegatenimplementierung in Swift umfasst drei Schritte: Deklaration des Protokolls, Erstellen einer schwachen delegate-Eigenschaft im Besitzer und Implementierung des Protokolls in der Delegatenklasse. Betrachten wir ein Beispiel eines benutzerdefinierten UITextField mit Validierung.
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("Validierung fehlgeschlagen: Eingabe zu kurz")
}
}LoginViewController implementiert das ValidatorDelegate-Protokoll und setzt sich selbst als Delegaten für textField. Bei jeder Textänderung ruft ValidatedTextField validate(_:) auf, und wenn die Validierung fehlschlägt — validatorDidFail(_:). Der Controller fungiert als Vermittler zwischen der View und der Validierungslogik.
Die Wahl zwischen Delegat, Benachrichtigungen und Closures hängt von der Anzahl der Empfänger und der Komponentenkopplung ab. Jeder Mechanismus löst das Problem der Kommunikation zwischen Objekten, jedoch mit unterschiedlichen Kompromissen.
| Eigenschaft | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| Kommunikationstyp | 1:1 | 1:N | 1:1 |
| Kopplung | Schwach (über Protokoll) | Sehr schwach (String-Schlüssel) | Mittel (Kontextaufnahme) |
| Typsicherheit | Vollständig | Keine (Any?) | Vollständig |
| Retain-Zyklus-Risiko | Nein (weak) | Nein | Ja (self-Aufnahme) |
| Wann verwenden | Komplexe Callbacks mit mehreren Methoden | Ereignisse, die viele interessieren | Einfache Closures mit 1-2 Callbacks |
Delegate ist optimal, wenn Sie eine Reihe verwandter Ereignisse an einen einzelnen Empfänger übergeben müssen. NotificationCenter eignet sich besser für Broadcast-Benachrichtigungen. Closure ist für einfache asynchrone Operationen wie Completion-Handler in URLSession gedacht.
Objective-C verwendet @protocol und @optional zur Deklaration von Delegaten. Anders als in Swift sind alle Protokollmethoden standardmäßig optional. Der Hauptunterschied ist der Aufruf von respondsToSelector: vor dem Senden einer Nachricht an den Delegaten, da die Methode möglicherweise nicht implementiert ist.
@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];
}
// asynchrones Bildladen
}
@endDer Hauptunterschied in Objective-C: Vor dem Aufruf einer optionalen Methode ist eine respondsToSelector:-Prüfung erforderlich. In Swift machen optionale Protokollmethoden diese Prüfung überflüssig — optional chaining (?.) behandelt das Fehlen einer Implementierung automatisch.
Fehler bei der Verwendung von Delegaten führen zu Speicherlecks, App-Abstürzen und nicht offensichtlichen Fehlern. Betrachten wir die fünf häufigsten Probleme.
Retain-Zyklus ist der häufigste Fehler. Wenn die delegate-Eigenschaft als strong deklariert ist und der Delegat wiederum das Besitzerobjekt besitzt, entsteht ein retain-Zyklus. Beide Objekte werden niemals aus dem Speicher freigegeben. Lösung: Deklarieren Sie den Delegaten immer als weak var in Swift oder @property (weak) in Objective-C.
Wenn das Besitzerobjekt den Delegaten überlebt und die Referenz erhalten bleibt, führt der Aufruf der Delegatenmethode zu EXC_BAD_ACCESS. Die schwache Referenz löst dieses Problem automatisch: Nach der Freigabe des Delegaten wird die Eigenschaft nil. In Multithread-Szenarien sollten Sie den Delegaten jedoch zusätzlich im Hauptthread überprüfen.
Ein Protokoll mit über 20 Methoden verstößt gegen das Interface-Segregation-Prinzip (ISP). UITableViewDelegate enthält etwa 30 optionale Methoden — dies ist eine historische Ausnahme. In eigenen Protokollen ist es besser, die Verantwortung auf mehrere kleinere Protokolle aufzuteilen, jedes mit seiner eigenen Rolle.
Die System-APIs von Apple nutzen das Delegate-Muster aktiv. Betrachten wir drei wichtige Beispiele aus UIKit, die in jeder iOS-Anwendung vorkommen.
| API | Protokoll | Wichtige Methoden |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
Jedes dieser Protokolle implementiert verschiedene Aspekte des Verhaltens: UITableViewDelegate verwaltet Aussehen und Berührungsreaktion, UITextFieldDelegate steuert die Texteingabe, URLSessionDelegate behandelt Netzwerkereignisse. Dies zeigt die Flexibilität des Musters: Der Delegat kann an jeden Verantwortungsbereich angepasst werden.
Häufig gestellte Fragen
Delegate verwaltet Verhalten und Aussehen (Zellenhöhe, Berührungsreaktion). DataSource liefert Daten (Anzahl der Zeilen, Zellinhalte). In UITableViewDelegate und UITableViewDataSource — dies sind zwei separate Protokolle, die die Verantwortung für Darstellung und Daten aufteilen.
Die schwache Referenz verhindert retain-Zyklen. Der Besitzer (z.B. UITableView) hält eine Referenz auf den Delegaten nur als weak. Wenn der Delegat (UIViewController) die Tabelle besitzt, würde eine starke Referenz auf den Delegaten einen Zyklus erzeugen: ViewController → UITableView → Delegate (ViewController). Weak unterbricht diesen Zyklus.
In SwiftUI wird das Delegate-Muster seltener verwendet — es wird durch @Binding, @State und Closures ersetzt. Allerdings wird der Delegat weiterhin für die UIKit-Integration über UIViewRepresentable verwendet. Beispielsweise bleiben MKMapViewDelegate und WKUIDelegate relevant, wenn UIKit-Komponenten in SwiftUI eingebettet werden.
@objc optional ermöglicht die Deklaration optionaler Methoden in einem Swift-Protokoll. Dies ist ein Kompatibilitätsmechanismus mit der Objective-C-Laufzeit. Ohne @objc sind alle Swift-Protokollmethoden standardmäßig erforderlich. Optional wird in UIKit-Protokollen verwendet, bei denen der Delegat nur die benötigten Methoden implementieren kann.
Ein Objekt kann nur einen Delegaten für jede delegate-Eigenschaft haben. Wenn Sie mehrere Objekte benachrichtigen müssen, verwenden Sie einen Multicast-Delegaten, ein Array von Delegaten oder NotificationCenter. Das Delegate-Muster wurde ursprünglich als 1:1-Beziehung konzipiert.
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