Delegate — egy tervezési minta, amelyben egy objektum egy feladat végrehajtását egy másik objektumra delegálja. iOS-ben a minta Swift protokollokon és @protocol Objective-C-n keresztül valósul meg. A Delegation a Cocoa Touch egyik alapvető mintája, amelyet a UITableViewDelegate, UITextFieldDelegate és több száz más Apple API használ. Az Apple Developer Documentation (2025) szerint az UIKit rendszerosztályainak körülbelül 70%-a delegáltakat használ a viselkedés testreszabásához öröklés nélkül.
Fő pontok
Delegate — egy viselkedéstervezési minta, amely lehetővé teszi egy objektum számára, hogy felelősségeinek egy részét egy másik objektumra ruházza. Az örökléstől eltérően, ahol a gyermekosztály felülírja a szülő metódusait, a delegált kompozíciót használ: a tulajdonos objektum tárol egy referenciát a delegáltra, és meghívja annak metódusait meghatározott pontokon.
A delegált meghatározza a protokollt — egy metóduskészletet, amelyet a delegált megvalósíthat. A metódusok kötelező (required) és opcionális (optional) kategóriákra oszlanak. Swift-ben a protokoll opcionális metódusait az @objc optional kulcsszó jelöli.
Az A objektum (tulajdonos) tartalmazza a delegate tulajdonságot — egy weak referenciát a B objektumra (delegált). Amikor egy esemény történik, A ellenőrzi, hogy B implementálja-e a megfelelő protokollmetódust, és meghívja azt. A weak referencia kötelező: nélküle a delegált nem szabadítható fel a memóriából, mivel a tulajdonos erős referenciával tartja.
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)
// aszinkron betöltés
}
}A DataLoader osztály meghatározza a LoaderDelegate protokollt, és meghívja a delegált metódusait a betöltési életciklus kulcsfontosságú pontjain. Az AnyObject garantálja, hogy a protokoll csak osztályok által implementálható — ez szükséges a weak referenciához.
A delegált megvalósítása Swift-ben három lépést foglal magában: a protokoll deklarálása, a weak delegate tulajdonság létrehozása a tulajdonosban, és a protokoll implementálása a delegált osztályban. Tekintsük meg egy egyéni UITextField példáján validációval.
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("Érvényesítés sikertelen: a bemenet túl rövid")
}
}A LoginViewController implementálja a ValidatorDelegate protokollt, és beállítja magát a textField delegáltjának. Minden szövegváltozáskor a ValidatedTextField meghívja a validate(_:)-t, és ha a validáció sikertelen — validatorDidFail(_:)-t. A Controller közvetítőként működik a View és a validációs logika között.
A választás delegált, értesítések és closure-ök között a címzettek számától és a komponensek kapcsolódási fokától függ. Minden mechanizmus megoldja az objektumok közötti kommunikáció problémáját, de különböző kompromisszumokkal.
| Jellemző | Delegate | NotificationCenter | Closure |
|---|---|---|---|
| Kapcsolat típusa | 1:1 | 1:N | 1:1 |
| Kapcsolódás | Gyenge (protokollon keresztül) | Nagyon gyenge (karakterlánc-kulcs) | Közepes (kontextus rögzítése) |
| Típusbiztonság | Teljes | Nincs (Any?) | Teljes |
| Retain cycle kockázat | Nem (weak) | Nem | Igen (self rögzítése) |
| Mikor alkalmazzuk | Összetett callback-ek több metódussal | Események, amelyek sokakat érdekelnek | Egyszerű closure-ök 1-2 callback-kel |
Delegate optimális, amikor egy sor kapcsolódó eseményt kell továbbítani egy címzettnek. NotificationCenter jobb broadcast értesítésekhez. Closure — egyszerű aszinkron műveletekhez, mint a completion handler az URLSession-ben.
Objective-C a @protocol és @optional használatával deklarálja a delegáltakat. A Swift-től eltérően a protokoll összes metódusa alapértelmezés szerint opcionális. A legfontosabb különbség — a respondsToSelector: meghívása, mielőtt üzenetet küldene a delegáltnak, mivel a metódus lehet, hogy nincs implementálva.
@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];
}
// aszinkron képbetöltés
}
@endA legfontosabb különbség Objective-C-ben: egy opcionális metódus meghívása előtt ellenőrizni kell a respondsToSelector:-t. Swift-ben a protokoll opcionális metódusai kiküszöbölik ezt az ellenőrzést — az optional chaining (?.) automatikusan kezeli az implementáció hiányának esetét.
A delegáltak használatának hibái memóriaszivárgáshoz, az alkalmazás összeomlásához és nem nyilvánvaló hibákhoz vezetnek. Tekintsük az öt leggyakoribb problémát.
Retain cycle — a leggyakoribb hiba. Ha a delegate tulajdonság strong-ként van deklarálva, és a delegált pedig a tulajdonos objektum tulajdonosa, egy megtartási ciklus jön létre. Mindkét objektum soha nem szabadul fel a memóriából. Megoldás: mindig deklarálja a delegáltat weak var-ként Swift-ben vagy @property (weak)-ként Objective-C-ben.
Ha a tulajdonos objektum túléli a delegáltat, és a referencia megmarad, a delegált metódusának meghívása EXC_BAD_ACCESS-t okoz. A weak referencia automatikusan megoldja ezt a problémát: a delegált felszabadítása után a tulajdonság nil lesz. Többszálú forgatókönyvekben azonban érdemes a delegáltat a fő szálon is ellenőrizni.
A 20+ metódussal rendelkező protokoll megsérti az interfész elkülönítés elvét (ISP). Az UITableViewDelegate körülbelül 30 opcionális metódust tartalmaz — ez történelmi kivétel. Saját protokolljaiban jobb a felelősséget több kis protokollra osztani, mindegyiknek saját szereppel.
Az Apple rendszer API-i aktívan használják a Delegate mintát. Tekintsünk három kulcsfontosságú példát az UIKit-ből, amelyek minden iOS alkalmazásban megtalálhatók.
| API | Protokoll | Kulcs metódusok |
|---|---|---|
| UITableView | UITableViewDelegate | didSelectRowAt, heightForRowAt, willDisplay |
| UITextField | UITextFieldDelegate | shouldChangeCharactersIn, didBeginEditing, shouldReturn |
| URLSession | URLSessionDelegate | didReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError |
Ezen protokollok mindegyike a viselkedés különböző aspektusait valósítja meg: UITableViewDelegate kezeli a megjelenést és az érintésre adott reakciót, UITextFieldDelegate vezérli a szövegbevitelt, URLSessionDelegate feldolgozza a hálózati eseményeket. Ez mutatja a minta rugalmasságát: a delegált bármilyen felelősségi területhez igazítható.
Gyakran Ismételt Kérdések
Delegate kezeli a viselkedést és a megjelenést (cella magassága, érintésre adott reakció). DataSource biztosítja az adatokat (sorok száma, cellák tartalma). Az UITableViewDelegate és UITableViewDataSource — két külön protokoll, amelyek megosztják a felelősséget a megjelenítés és az adatok között.
A weak referencia megakadályozza a retain cycle-t. A tulajdonos (pl. UITableView) csak weak-ként tartja a referenciát a delegáltra. Ha a delegált (UIViewController) birtokolja a táblázatot, egy erős referencia a delegáltra ciklust hozna létre: ViewController → UITableView → Delegate (ViewController). A Weak megszakítja ezt a ciklust.
A SwiftUI-ban a Delegate minta ritkábban használt — a @Binding, @State és closure-ök helyettesítik. Azonban a delegate továbbra is használatos az UIKit integrációhoz UIViewRepresentable-en keresztül. Például az MKMapViewDelegate és WKUIDelegate továbbra is relevánsak az UIKit komponensek SwiftUI-ba csomagolásakor.
@objc optional lehetővé teszi opcionális metódusok deklarálását egy Swift protokollban. Ez egy kompatibilitási mechanizmus az Objective-C runtime-tal. @objc nélkül a Swift protokoll összes metódusa alapértelmezés szerint kötelező. Az Optional az UIKit protokollokban használatos, ahol a delegált csak a szükséges metódusokat implementálhatja.
Egy objektumnak csak egy delegáltja lehet minden delegate tulajdonsághoz. Ha több objektumot kell értesíteni, használjon multicast delegate-t, delegáltak tömbjét vagy NotificationCenter-t. A Delegate minta alapvetően 1:1 kapcsolatként lett tervezve.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is