Delegate — is een ontwerppatroon waarbij een object de uitvoering van een taak delegeert aan een ander object. In iOS wordt het patroon geïmplementeerd via Swift-protocollen en @protocol Objective-C. Delegation is een van de fundamentele patronen van Cocoa Touch, gebruikt in UITableViewDelegate, UITextFieldDelegate en honderden andere Apple API's. Volgens Apple Developer Documentation (2025) gebruikt ongeveer 70% van de systeemklassen van UIKit delegaten om gedrag aan te passen zonder overerving.
Belangrijkste punten
Delegate — is een gedragsontwerppatroon waarmee een object een deel van zijn verantwoordelijkheden kan overdragen aan een ander object. In tegenstelling tot overerving, waarbij de subclass methoden van de ouder overschrijft, gebruikt de delegaat compositie: het eigenaars-object slaat een referentie naar de delegaat op en roept zijn methoden op specifieke punten aan.
De delegaat definieert een protocol — een reeks methoden die de delegaat kan implementeren. Methoden zijn onderverdeeld in verplichte (required) en optionele (optional). In Swift worden optionele protocolmethoden gemarkeerd met het sleutelwoord @objc optional.
Object A (eigenaar) bevat de eigenschap delegate — een weak-referentie naar object B (delegaat). Wanneer een gebeurtenis plaatsvindt, controleert A of B de bijbehorende protocolmethode implementeert en roept deze aan. Weak-referentie is verplicht: zonder deze kan de delegaat niet uit het geheugen worden bevrijd, omdat de eigenaar hem met een strong-referentie vasthoudt.
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)
// asynchrone load
}
}De klasse DataLoader definieert het protocol LoaderDelegate en roept de methoden van de delegaat aan op belangrijke punten in de laadcyclus. AnyObject garandeert dat het protocol alleen door klassen kan worden geïmplementeerd — dit is noodzakelijk voor de weak-referentie.
Implementatie van de delegaat in Swift omvat drie stappen: het declareren van het protocol, het creëren van de weak delegate-eigenschap in de eigenaar en het implementeren van het protocol in de delegatenklasse. Laten we het bekijken aan de hand van een aangepaste UITextField met validatie.
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("Validatie mislukt: invoer te kort")
}
}LoginViewController implementeert het protocol ValidatorDelegate en stelt zichzelf aan als delegaat van textField. Bij elke tekstwijziging roept ValidatedTextField validate(_:) aan, en als de validatie mislukt — validatorDidFail(_:). Controller fungeert als tussenpersoon tussen de View en de validatielogica.
De keuze tussen delegaten, meldingen en closures hangt af van het aantal ontvangers en de mate van koppeling van componenten. Elk mechanisme lost het probleem van communicatie tussen objecten op, maar met verschillende afwegingen.
| Kenmerk | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| Type verbinding | 1:1 | 1:N | 1:1 |
| Koppeling | Zwak (via protocol) | Zeer zwak (string-sleutel) | Gemiddeld (context vastleggen) |
| Typeveiligheid | Volledig | Afwezig (Any?) | Volledig |
| Retain cycle risico | Nee (weak) | Nee | Ja (self vastleggen) |
| Wanneer toepassen | Complexe callbacks met meerdere methoden | Gebeurtenissen die velen interesseren | Eenvoudige closures met 1-2 callbacks |
Delegate is optimaal wanneer een reeks gerelateerde gebeurtenissen aan één ontvanger moet worden doorgegeven. NotificationCenter is beter voor broadcast-meldingen. Closure — voor eenvoudige asynchrone bewerkingen, zoals een completion handler in URLSession.
Objective-C gebruikt @protocol en @optional voor het declareren van delegaten. In tegenstelling tot Swift zijn alle methoden van het protocol standaard optioneel. Het belangrijkste verschil is de aanroep van respondsToSelector: voordat een bericht naar de delegaat wordt gestuurd, omdat de methode mogelijk niet is geïmplementeerd.
@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];
}
// asynchrone afbeelding laden
}
@endHet belangrijkste verschil in Objective-C: vóór het aanroepen van een optionele methode is controle met respondsToSelector: vereist. In Swift elimineren optionele protocolmethoden deze controle — optional chaining (?.) handelt automatisch het geval van ontbrekende implementatie af.
Fouten bij het gebruik van delegaten leiden tot geheugenlekken, crashes van de app en onduidelijke bugs. Laten we de vijf meest voorkomende problemen bekijken.
Retain cycle — de meest voorkomende fout. Als de eigenschap delegate is gedeclareerd als strong en de delegaat op zijn beurt het eigenaars-object bezit, ontstaat er een retentiecyclus. Beide objecten worden nooit uit het geheugen bevrijd. Oplossing: declareer de delegaat altijd als weak var in Swift of @property (weak) in Objective-C.
Als het eigenaars-object de delegaat overleeft en de referentie blijft bestaan, leidt het aanroepen van de delegatenmethode tot EXC_BAD_ACCESS. Weak-referentie lost dit probleem automatisch op: na het vrijgeven van de delegaat wordt de eigenschap nil. In multithreading-scenario's is het echter raadzaam om de delegaat ook in de hoofdthread te controleren.
Een protocol met meer dan 20 methoden schendt het principe van interfacescheiding (ISP). UITableViewDelegate bevat ongeveer 30 optionele methoden — dit is een historische uitzondering. In eigen protocollen is het beter om de verantwoordelijkheid te verdelen over meerdere kleine protocollen, elk met zijn eigen rol.
Systeem-API's van Apple gebruiken actief het Delegate-patroon. Laten we drie belangrijke voorbeelden uit UIKit bekijken die in elke iOS-app voorkomen.
| API | Protocol | Belangrijkste methoden |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
Elk van deze protocollen implementeert verschillende aspecten van gedrag: UITableViewDelegate beheert het uiterlijk en de reactie op aanrakingen, UITextFieldDelegate controleert de tekstinvoer, URLSessionDelegate verwerkt netwerkgebeurtenissen. Dit toont de flexibiliteit van het patroon: de delegaat kan worden aangepast aan elk verantwoordelijkheidsgebied.
Veelgestelde vragen
Delegate beheert het gedrag en uiterlijk (celhoogte, reactie op aanraking). DataSource levert gegevens (aantal rijen, celinhoud). In UITableViewDelegate en UITableViewDataSource zijn dit twee afzonderlijke protocollen die de verantwoordelijkheid voor presentatie en gegevens verdelen.
Weak-referentie voorkomt retain cycle. De eigenaar (bijv. UITableView) houdt de referentie naar de delegaat alleen als weak vast. Als de delegaat (UIViewController) de tabel bezit, zou een strong-referentie naar de delegaat een cyclus creëren: ViewController → UITableView → Delegate (ViewController). Weak verbreekt deze cyclus.
In SwiftUI wordt het Delegate-patroon minder vaak gebruikt — het wordt vervangen door @Binding, @State en closures. Echter, delegate wordt nog steeds gebruikt voor UIKit-integratie via UIViewRepresentable. Bijvoorbeeld MKMapViewDelegate en WKUIDelegate blijven relevant bij het inpakken van UIKit-componenten in SwiftUI.
@objc optional maakt het mogelijk om optionele methoden in een Swift-protocol te declareren. Dit is een compatibiliteitsmechanisme met Objective-C runtime. Zonder @objc zijn alle methoden van een Swift-protocol standaard verplicht. Optional wordt gebruikt in UIKit-protocollen, waar de delegaat alleen de methoden kan implementeren die hij nodig heeft.
Eén object kan slechts één delegaat hebben voor elke delegate-eigenschap. Als meerdere objecten moeten worden geïnformeerd, gebruik dan multicast delegate, een array van delegaten of NotificationCenter. Het Delegate-patroon is oorspronkelijk ontworpen als een 1:1-relatie.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook