Delegate: ce este, modelul de delegare și cum funcționează pe iOS

Autor: IT Sectr Publicat: 2026-03-17 Timp de citire: 10 min

Delegate — este un model de proiectare în care un obiect delegă executarea sarcinilor unui alt obiect printr-un protocol cu metode predefinite. În dezvoltarea iOS, Delegate este unul dintre modelele fundamentale ale Cocoa Touch, utilizat pentru notificarea asincronă fără conexiune directă între expeditor și destinatar. Conform Apple Documentation (2025), delegarea este aplicată în Foundation și UIKit pentru procesarea evenimentelor de tabel, cererilor de rețea și gestionării locației. Modelul asigură cuplarea slabă a componentelor și reutilizarea codului.

Principalele puncte

  • Delegate — modelul în care obiectul încredințează procesarea evenimentelor unui alt obiect printr-un protocol.
  • Protocol în Swift definește setul de metode pe care delegate trebuie sau le poate implementa.
  • Weak reference este obligatorie pentru proprietatea delegate pentru a evita retain cycle.
  • @objc optional permite ca metodele protocolului să fie opționale pentru implementare.
  • URLSessionDelegate — delegate asincron pentru procesarea evenimentelor cererii de rețea.

Ce este Delegate?

Delegate (delegat) — este un obiect care implementează un protocol specific și primește notificări despre evenimentele unui alt obiect. Modelul Delegation este o alternativă la moștenire: în loc să creeze o subclasă pentru suprascrierea metodelor, obiectul delegă procesarea evenimentelor unui obiect extern. În iOS, delegation este implementat prin protocoale Swift cu metode obligatorii și opționale. Proprietatea delegate este întotdeauna declarată ca weak var pentru a evita referințele ciclice între obiecte.

Definirea protocolului Delegate

Protocolul delegate definește contractul de interacțiune între obiecte. Metodele obligatorii trebuie implementate de delegat, altfel codul nu se va compila. Metodele opționale sunt marcate cu atributul @objc optional și permit delegatului să reacționeze doar la evenimentele necesare. Numele metodelor urmează convenția: primul parametru — obiectul-expeditor, al doilea — datele evenimentului. De exemplu, tableView(_:didSelectRowAt:) indică că expeditorul este UITableView, iar datele sunt indexul rândului selectat.

swift
// Protocolul Delegate
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)
}

// Clasa care folosește Delegate
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()
    }
}

Referință slabă la delegate

Proprietatea delegate trebuie declarată ca weak var (pointer slab) pentru a preveni retain cycle. Dacă referința ar fi puternică, delegatul și obiectul delegator s-ar reține reciproc, iar ARC nu ar putea elibera memoria lor. Protocoalele delegate moștenesc AnyObject (doar pentru clase), ceea ce permite utilizarea weak. Structurile și enum nu pot fi delegați din cauza semanticii valorilor. Alternativa pentru value types — callback closures.

swift
class ViewController: DownloadManagerDelegate {
    let manager = DownloadManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self // weak — fără retain cycle
        manager.startDownload(from: url)
    }

    func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
        processData(data)
    }

    func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
        showError(error)
    }
}

Cum funcționează modelul Delegate în iOS?

Modelul Delegate funcționează pe principiul „unu-la-unu": un obiect-expeditor poate avea un singur delegat la un moment dat. Când apare un eveniment, expeditorul verifică dacă delegate este setat și apelează metoda corespunzătoare a protocolului. Avantajul față de apelul direct — expeditorul nu cunoaște tipul delegatului, doar că acesta corespunde protocolului. Aceasta respectă principiul inversiunii dependențelor (DIP) din SOLID.

Ciclul de viață al delegatului

Delegatul este setat prin atribuire: someObject.delegate = self. La dealocarea delegatului, proprietatea devine automat nil datorită semanticii weak. Înainte de apelarea metodei delegate, se verifică prin optional chaining: delegate?.method(). Dacă delegate este nil — apelul este ignorat fără crash. Pentru metodele opționale ale protocolului se utilizează o verificare suplimentară: delegate?.responds(to: #selector(...)), deși în Swift această verificare este de obicei implicită prin declarația metodei opționale.

Notificarea asincronă prin Delegate

În mediul multi-thread, delegate este utilizat pentru returnarea asincronă a rezultatului. URLSession oferă URLSessionDelegate cu metode apelate la primirea datelor, timeout sau eroare de autentificare. Metodele delegate se execută în coada de fundal a URLSession, deci este necesară dispatcherea pe main queue pentru actualizări UI. Delegate asincron nu blochează firul de execuție apelant, permițând continuarea altor sarcini.

swift
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 vs Callback: compararea abordărilor

Delegate și Callback rezolvă aceeași sarcină — notificarea asincronă — dar în moduri diferite. Delegate folosește un protocol cu metode denumite, callback — o closure cu capturarea contextului. Alegerea depinde de numărul de evenimente, complexitatea semnăturilor și preferințele arhitecturale. Apple recomandă delegate pentru API-uri cu evenimente multiple (UITableView — 20+ metode) și callback pentru finalizări unice.

Când Delegate câștigă

Delegate este preferat atunci când trebuie procesate mai multe evenimente diferite de la o singură sursă. De exemplu, CLLocationManager notifică delegatul despre schimbarea locației, erori de permisiuni, intrarea/ieșirea din zone geografice și schimbarea statusului serviciilor. Fiecare eveniment este o metodă separată a protocolului cu nume clar și parametri tipizați. Delegate este, de asemenea, convenabil pentru configurarea comportamentului (metode should, will, did).

Când Callback câștigă

Callback este mai simplu pentru cereri unice cu un singur rezultat. Completion handler în URLSession.dataTask ocupă un rând în punctul de apel față de cel puțin trei metode ale protocolului. Callback este, de asemenea, mai natural pentru lanțuri funcționale (map, flatMap, async/await). Cu toate acestea, la înnestirea de peste 2-3 niveluri, callback se transformă în Callback Hell, în timp ce delegate rămâne întotdeauna plat.

Delegați încorporați în iOS SDK

iOS SDK conține zeci de protocoale delegate încorporate pentru diverse subsisteme. Fiecare dintre ele este proiectat pentru un scenariu specific de interacțiune. Conform Apple Documentation (2025), cei mai utilizați delegați sunt UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate și UNUserNotificationCenterDelegate. Aceste protocoale conțin între 3 și 30 de metode cu diferite grade de obligativitate.

UITableViewDelegate

UITableViewDelegate gestionează aspectul și comportamentul celulelor tabelului. Conține metode pentru procesarea selecției rândurilor, setarea înălțimii celulelor, vizualizări personalizate header/footer și acțiuni de glisare. Toate metodele protocolului sunt opționale, ceea ce permite implementarea doar a funcționalității necesare. În absența delegatului, tabelul funcționează cu setările implicite. Istoric, delegate se combina cu UITableViewDataSource.

URLSessionDelegate

URLSessionDelegate asigură control detaliat asupra cererilor HTTP. Metodele delegatului sunt apelate la primirea răspunsului serverului, datelor, finalizării descărcării. Subprotocoalele specializate URLSessionTaskDelegate și URLSessionDataDelegate extind funcționalitatea de bază pentru tipuri specifice de sarcini. Delegate este necesar pentru suportul descărcărilor în fundal, certificatelor SSL și procesării personalizate a redirecționărilor.

DelegateMetodeDestinație
UITableViewDelegate25Aspectul și interacțiunea cu tabelul
UITextFieldDelegate8Procesarea intrării textului și a tastaturii
CLLocationManagerDelegate12Actualizarea locației și zone geografice
URLSessionDelegate6Gestionarea sesiunii HTTP și a certificatelor
UNUserNotificationCenterDelegate4Procesarea notificărilor push în prim-plan

Gestionarea memoriei la lucrul cu Delegate

Gestionarea memoriei este un aspect critic al lucrului cu delegate în iOS. ARC (Automatic Reference Counting) gestionează automat memoria, dar numai cu utilizarea corectă a referințelor weak/unowned. Încălcarea regulilor duce la scurgeri de memorie sau dealocare prematură. Delegate declarat ca strong creează un retain cycle dacă proprietarul delegatului păstrează și o referință către obiectul delegator.

Retain cycle prin Delegate

Retain cycle apare atunci când obiectul A (proprietarul) se setează pe sine ca delegat al obiectului B, iar B păstrează o referință puternică către delegate. Exemplu: ViewController creează URLSession, setează self ca delegat al sesiunii, dar URLSession în mod implicit păstrează o referință puternică către delegate dacă nu este specificat delegateQueue. Soluția — verificați întotdeauna documentația API pentru tipul referinței către delegate (weak sau strong) și setați explicit delegate la nil în deinit.

swift
class SafeViewController: UIViewController {
    private var session: URLSession?
    private var service: NetworkService?

    override func viewDidLoad() {
        super.viewDidLoad()
        service = NetworkService()
        service?.delegate = self
    }

    deinit {
        // Resetarea delegate în deinit — best practice
        service?.delegate = nil
        session?.invalidateAndCancel()
    }
}

// URLSession cu weak delegate prin 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
        )
    }
}

Verificarea sigură a delegate înainte de apel

Înainte de apelarea metodei delegatului, trebuie verificat că delegatul există (nu este nil) și că implementează metoda apelată. Pentru metodele obligatorii ale protocolului, verificarea nu este necesară — compilatorul garantează implementarea. Pentru metodele opționale utilizați respond(to:) sau optional chaining. Dacă delegatul a fost dealocat, referința weak devine automat nil, iar apelul delegatului este ignorat. Acesta este un comportament sigur care nu necesită procesare suplimentară.

Erori tipice la implementarea Delegate

Dezvoltătorii comit adesea erori la lucrul cu modelul Delegate, mai ales în fazele incipiente ale învățării iOS. Cele mai frecvente: retain cycle din cauza strong delegate, uitarea apelului delegate?.method(), semnătura incorectă a metodelor protocolului, setarea delegate după începerea operației și coliziuni multi-thread. Să examinăm fiecare eroare și modalitățile de prevenire a acesteia.

Strong reference în loc de weak

Cea mai critică eroare — declararea proprietății delegate ca strong var în loc de weak var. Aceasta creează un retain cycle în care nici delegatul, nici obiectul delegator nu pot fi eliberați. Consecințe: scurgere de memorie, încetinirea aplicației și bug-uri ascunse. Soluția: utilizați întotdeauna weak var pentru delegate, iar protocolul moșteniți-l de la AnyObject pentru a exclude utilizarea value types ca delegat.

Setarea delegate după pornirea operației

Dacă delegate este setat după apelarea unei metode asincrone, primele evenimente pot fi pierdute. Exemplu: apelarea startDownload() înainte de atribuirea manager.delegate = self duce la pierderea callback-ului de finalizare, dacă descărcarea se execută sincron sau foarte rapid. Soluția: setați delegate înainte de apelarea metodei asincrone și documentați ordinea inicializării în comentariile protocolului.

Întrebări frecvente

De ce delegate este declarat ca weak?

Weak previne retain cycle între delegat și obiectul delegator. Dacă referința ar fi strong, obiectele s-ar reține reciproc, iar ARC nu le-ar putea elibera. Referința weak devine automat nil la dealocarea delegatului. Aceasta este o practică standard Cocoa Touch din perioada Objective-C și a fost păstrată în Swift pentru compatibilitate inversă.

Care este diferența dintre delegate și dataSource?

Delegate procesează evenimentele și gestionează comportamentul (înălțimea celulelor, reacția la click-uri). DataSource furnizează datele pentru afișare (numărul de rânduri, celulele). Delegate răspunde la întrebarea „cum?", dataSource — la întrebarea „ce?". În iOS, ambele sunt implementate prin protocoale, adesea în același controler, dar conceptual sunt separate.

Poate fi folosit struct ca delegate?

Nu, dacă protocolul moștenește AnyObject (protocol de clasă). Referințele weak sunt disponibile doar pentru reference types (clase). Pentru value types (struct, enum) utilizați callback closure-uri sau o clasă wrapper separată. Dacă controlați protocolul, puteți să nu moșteniți AnyObject, dar atunci weak este interzis — alegeți conștient între delegate weak și struct-delegat.

Ce este metoda responds(to:) și la ce servește?

responds(to:) — este o metodă NSObjectProtocol care verifică dacă obiectul implementează selectorul specificat. Este utilizată pentru verificarea metodelor opționale @objc ale protocolului înainte de apel. Fără această verificare, apelarea unei metode opționale neimplementate va duce la NSInvalidArgumentException. În Swift, pentru protocoalele cu @objc optional, verificarea poate fi implicită prin optional binding.

Delegate este un singleton?

Nu, delegate este un model de delegare, nu un singleton. Spre deosebire de singleton, delegatul poate fi înlocuit în runtime și există într-o singură instanță pentru fiecare obiect delegator. Un obiect poate fi delegat pentru mai mulți expeditori. Singleton este un model de creare care garantează o singură instanță a clasei și nu are nicio legătură cu delegarea.

Rezumat

  • Delegate — modelul în care obiectul delegă procesarea evenimentelor unui alt obiect printr-un protocol cu metode tipizate.
  • Weak var este obligatoriu pentru proprietatea delegate pentru a preveni retain cycle și scurgerile de memorie.
  • Protocolul definește metodele obligatorii și opționale (@objc optional) de delegare.
  • iOS SDK conține 15+ protocoale delegate încorporate: UITableViewDelegate, URLSessionDelegate, CLLocationManagerDelegate.
  • Delegate este preferat față de callback la 3+ evenimente diferite de la o singură sursă (CLLocationManager).
  • Delegate asincron este utilizat în URLSession pentru obținerea datelor și progresului fără blocarea firului de execuție.
  • Setați delegate înainte de pornirea operației asincrone și resetați-l în deinit pentru lucrul sigur cu memoria.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și