Delegate — egy tervezési minta, amelyben egy objektum egy másik objektumra delegálja a feladatok végrehajtását egy előre meghatározott metódusokkal rendelkező protokollon keresztül. Az iOS fejlesztésben a Delegate a Cocoa Touch egyik alapvető mintája, amelyet aszinkron értesítésre használnak a küldő és a fogadó közötti közvetlen kapcsolat nélkül. Az Apple Documentation (2025) szerint a delegálást a Foundation-ben és UIKit-ben alkalmazzák táblaesemények, hálózati kérések és helykezelés feldolgozására. A minta laza kapcsolódást biztosít a komponensek között és a kód újrafelhasználását.
Főbb pontok
Delegate (delegált) — egy objektum, amely implementál egy adott protokollt, és értesítéseket kap egy másik objektum eseményeiről. A Delegation minta alternatívája az öröklésnek: ahelyett, hogy alosztályt hozna létre metódusok felülírásához, az objektum egy külső objektumra delegálja az események feldolgozását. Az iOS-ben a delegation Swift protokollokon keresztül van implementálva kötelező és opcionális metódusokkal. A delegate tulajdonság mindig weak var-ként van deklarálva a ciklikus hivatkozások elkerülése érdekében.
A delegate protokoll meghatározza az objektumok közötti interakciós szerződést. Kötelező metódusokat a delegáltnak implementálnia kell, különben a kód nem fordul le. Opcionális metódusok az @objc optional attribútummal vannak jelölve, és lehetővé teszik a delegált számára, hogy csak a szükséges eseményekre reagáljon. A metódusnevek konvenciót követnek: első paraméter — a küldő objektum, második — az esemény adatai. Például a tableView(_:didSelectRowAt:) azt jelzi, hogy a küldő UITableView, az adat pedig a kiválasztott sor indexe.
// 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)
}
// Delegate-et használó osztály
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()
}
}
A delegate tulajdonságot weak var (gyenge mutató) ként kell deklarálni a retain cycle megelőzése érdekében. Ha a hivatkozás erős lenne, a delegált és a delegáló objektum egymást tartaná, és az ARC nem tudná felszabadítani a memóriájukat. A delegate protokollok az AnyObject-ből (csak osztályok számára) örökölnek, ami lehetővé teszi a weak használatát. A struktúrák és enum-ok nem lehetnek delegáltak az értékszemantika miatt. Alternatíva value types számára — callback closure-ok.
class ViewController: DownloadManagerDelegate {
let manager = DownloadManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self // weak — nincs retain cycle
manager.startDownload(from: url)
}
func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
processData(data)
}
func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
showError(error)
}
}
A Delegate minta az „egy-az-egyhez” elv szerint működik: egy küldő objektumnak egyszerre csak egy delegáltja lehet. Amikor egy esemény bekövetkezik, a küldő ellenőrzi, hogy a delegate be van-e állítva, és meghívja a protokoll megfelelő metódusát. Előny a közvetlen hívással szemben — a küldő nem ismeri a delegált típusát, csak azt, hogy megfelel a protokollnak. Ez megfelel a függőséginverzió elvének (DIP) a SOLID-ból.
A delegált hozzárendeléssel kerül beállításra: someObject.delegate = self. A delegált felszabadításakor a tulajdonság automatikusan nil lesz a weak szemantika miatt. A delegate metódus meghívása előtt optional chaining-en keresztül ellenőrzésre kerül: delegate?.method(). Ha a delegate nil — a hívás figyelmen kívül lesz hagyva crash nélkül. A protokoll opcionális metódusaihoz további ellenőrzés használatos: delegate?.responds(to: #selector(...)), bár a Swift-ben ez az ellenőrzés általában implicit az opcionális metódus deklarációján keresztül.
Többszálas környezetben a delegate az eredmény aszinkron visszaadására szolgál. URLSession URLSessionDelegate-et biztosít metódusokkal, amelyek adatok fogadásakor, időtúllépéskor vagy hitelesítési hibakor hívódnak meg. A delegate metódusok az URLSession háttérvárólistájában hajtódnak végre, ezért a UI frissítésekhez a main queue-ra történő diszpécselés szükséges. Az aszinkron delegate nem blokkolja a hívó szálat, lehetővé téve más feladatok folytatását.
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 és Callback ugyanazt a feladatot oldja meg — aszinkron értesítés —, de különböző módon. A Delegate nevesített metódusokkal rendelkező protokollt használ, a callback — kontextusbefogással rendelkező closure-t. A választás az események számától, az aláírások bonyolultságától és az architekturális preferenciáktól függ. Az Apple a delegate-t ajánlja sok eseményű API-khoz (UITableView — 20+ metódus), a callback-t pedig egyszeri befejezésekhez.
A delegate előnyösebb, amikor több különböző eseményt kell feldolgozni egy forrásból. Például a CLLocationManager értesíti a delegáltat a helyváltozásról, engedélyhibákról, geozónákba való belépésről/kilépésről és a szolgáltatás állapotának változásáról. Minden esemény egy külön protokollmetódus egyértelmű névvel és típusos paraméterekkel. A delegate a viselkedés konfigurálásához is kényelmes (should, will, did metódusok).
A callback egyszerűbb egyszeri kérésekhez egyetlen eredménnyel. Completion handler az URLSession.dataTask-ben egy sort foglal el a hívási ponton, szemben a protokoll legalább három metódusával. A callback természetesebb a funkcionális láncokhoz is (map, flatMap, async/await). 2-3 szintnél mélyebb egymásba ágyazásnál azonban a callback Callback Hellé válik, míg a delegate mindig lapos marad.
Az iOS SDK több tucat beépített delegate protokollt tartalmaz különböző alrendszerek számára. Mindegyik egy adott interakciós forgatókönyvhöz készült. Az Apple Documentation (2025) szerint a leggyakrabban használt delegate-ek a UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate és UNUserNotificationCenterDelegate. Ezek a protokollok 3 és 30 közötti metódust tartalmaznak különböző kötelező jelleggel.
UITableViewDelegate kezeli a táblázatcellák megjelenését és viselkedését. Metódusokat tartalmaz sor kiválasztásának feldolgozásához, cellamagasság beállításához, egyéni header/footer nézetekhez és csúsztatási műveletekhez. A protokoll minden metódusa opcionális, ami lehetővé teszi csak a szükséges funkciók implementálását. Delegált hiányában a tábla alapértelmezett beállításokkal működik. Történelmileg a delegate a UITableViewDataSource-szal kombinálódott.
URLSessionDelegate részletes ellenőrzést biztosít a HTTP-kérések felett. A delegált metódusai a szerver válaszának, adatok fogadásakor, letöltés befejezésekor hívódnak meg. A specializált alprotokollok URLSessionTaskDelegate és URLSessionDataDelegate kiterjesztik az alapfunkcionalitást bizonyos feladattípusokra. A delegate szükséges a háttérletöltések, SSL-tanúsítványok és egyéni átirányítások támogatásához.
| Delegate | Metódus | Rendeltetés |
|---|---|---|
| UITableViewDelegate | 25 | Tábla megjelenése és interakció |
| UITextFieldDelegate | 8 | Szövegbevitel és billentyűzet feldolgozása |
| CLLocationManagerDelegate | 12 | Helyfrissítés és geozónák |
| URLSessionDelegate | 6 | HTTP-munkamenet és tanúsítványok kezelése |
| UNUserNotificationCenterDelegate | 4 | Push értesítések feldolgozása előtérben |
A memóriakezelés kritikus szempont a delegate használatakor iOS-ben. ARC (Automatic Reference Counting) automatikusan kezeli a memóriát, de csak a weak/unowned hivatkozások helyes használatával. A szabályok megsértése memóriaszivárgáshoz vagy idő előtti felszabadításhoz vezet. A strong-ként deklarált delegate retain cycle-t hoz létre, ha a delegált tulajdonosa is hivatkozást tart a delegáló objektumra.
Retain cycle akkor keletkezik, amikor az A objektum (tulajdonos) saját magát állítja be a B objektum delegáltjának, és B erős hivatkozást tart a delegate-re. Példa: A ViewController létrehoz egy URLSession-t, beállítja a self-et a munkamenet delegáltjának, de az URLSession alapértelmezés szerint erős hivatkozást tart a delegate-re, ha nincs megadva delegateQueue. Megoldás — mindig ellenőrizze az API dokumentációját a delegate-re való hivatkozás típusáért (weak vagy strong), és explicit állítsa nil-ra a delegate-t a deinit-ben.
class SafeViewController: UIViewController {
private var session: URLSession?
private var service: NetworkService?
override func viewDidLoad() {
super.viewDidLoad()
service = NetworkService()
service?.delegate = self
}
deinit {
// Delegate nilra állítása deinit-ben — best practice
service?.delegate = nil
session?.invalidateAndCancel()
}
}
// URLSession weak delegate-dzsel NSObject-en keresztül
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
)
}
}
A delegált metódusának meghívása előtt ellenőrizni kell, hogy a delegált létezik (nem nil) és implementálja a hívott metódust. A protokoll kötelező metódusainál ellenőrzés nem szükséges — a fordító garantálja az implementációt. Opcionális metódusoknál használja a respond(to:) vagy optional chaining függvényt. Ha a delegált felszabadításra került, a weak hivatkozás automatikusan nil lesz, és a delegált hívása figyelmen kívül marad. Ez biztonságos viselkedés, amely nem igényel további feldolgozást.
A fejlesztők gyakran követnek el hibákat a Delegate mintával való munkában, különösen az iOS tanulásának kezdeti szakaszában. Leggyakoribbak: retain cycle a strong delegate miatt, elfelejtett delegate?.method() hívás, a protokoll metódusok helytelen aláírása, a delegate beállítása a művelet megkezdése után, és több szálas ütközések. Vizsgáljuk meg az egyes hibákat és megelőzésük módjait.
A legkritikusabb hiba — a delegate tulajdonság deklarálása strong var-ként weak var helyett. Ez egy olyan retain cycle-t hoz létre, amelyben sem a delegált, sem a delegáló objektum nem szabadítható fel. Következmények: memóriaszivárgás, az alkalmazás lelassulása és rejtett hibák. Megoldás: mindig weak var-t használjon a delegate-hez, és a protokollt az AnyObject-ből származtassa, hogy kizárja a value types használatát delegáltként.
Ha a delegate egy aszinkron metódus meghívása után kerül beállításra, az első események elveszhetnek. Példa: a startDownload() meghívása a manager.delegate = self hozzárendelés előtt a befejezési callback elvesztéséhez vezet, ha a letöltés szinkron vagy nagyon gyors. Megoldás: állítsa be a delegate-t az aszinkron metódus meghívása előtt, és dokumentálja az inicializálási sorrendet a protokoll megjegyzéseiben.
Gyakran ismételt kérdések
A Weak megakadályozza a retain cycle-t a delegált és a delegáló objektum között. Ha a hivatkozás erős lenne, az objektumok egymást tartanák, és az ARC nem tudná felszabadítani őket. A weak-hivatkozás automatikusan nil lesz a delegált felszabadításakor. Ez a Cocoa Touch szokásos gyakorlata az Objective-C megjelenése óta, és a Swift-ben is megmaradt a visszafelé kompatibilitás érdekében.
Delegate feldolgozza az eseményeket és kezeli a viselkedést (cellamagasság, kattintásra adott reakció). DataSource adatokat szolgáltat a megjelenítéshez (sorok száma, cellák). A delegate a “hogyan?” kérdésre válaszol, a dataSource a “mit?” kérdésre. Az iOS-ben mindkettő protokollokon keresztül van implementálva, gyakran ugyanabban a vezérlőben, de fogalmilag elkülönül.
Nem, ha a protokoll az AnyObject-ből (osztályprotokoll) származik. A weak-hivatkozások csak reference types (osztályok) számára elérhetők. A value types (struct, enum) esetében használjon callback closure-okat vagy egy külön wrapper osztályt. Ha Ön irányítja a protokollt, nem származtathatja AnyObject-ből, de akkor a weak tiltott — válasszon tudatosan a weak-delegate és a struct-delegált között.
responds(to:) — az NSObjectProtocol metódusa, amely ellenőrzi, hogy az objektum implementálja-e a megadott selectort. A protokoll opcionális @objc metódusainak ellenőrzésére szolgál hívás előtt. Ezen ellenőrzés nélkül egy nem implementált opcionális metódus meghívása NSInvalidArgumentException-t eredményez. A Swift-ben az @objc optional protokollok esetén az ellenőrzés implicit lehet optional binding-en keresztül.
Nem, a delegate egy delegációs minta, nem singleton. Ellentétben a singleton-nal, a delegált futásidőben lecserélhető, és minden delegáló objektumhoz egy példányban létezik. Egy objektum több küldő számára is lehet delegált. A singleton egy létrehozási minta, amely egy osztály egyetlen példányát garantálja, és semmi köze a delegáláshoz.
Összefoglalás
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