defer — ce este, sintaxa și ordinea de execuție

Autor: IT Sectr Publicat: 2026-06-20 Timp de citire: 8 min

defer — o construcție de control în Swift care planifică executarea unui bloc de cod pentru momentul ieșirii din domeniul de vizibilitate curent (scope). Blocul defer se execută independent de modul de terminare — return, break, throw, fatalError sau terminare normală. Conform Swift Language Guide (2025), la prezența mai multor defer în același scope, acestea se execută în ordinea inversă a declarării — ultimul declarat se execută primul (LIFO). Acest lucru face defer indispensabil pentru curățarea garantată a resurselor: închiderea descriptoarelor de fișiere, eliminarea blocărilor, eliberarea pointerilor temporari fără riscul de a omite cleanup la ieșirea prematură.

Principalele

  • defer — bloc de cod executat la ieșirea din domeniul de vizibilitate, indiferent de motivul ieșirii (return, throw, break)
  • Ordinea LIFO: mai multe defer se execută de jos în sus — ultimul declarat se declanșează primul
  • Curățarea resurselor — aplicarea principală: închiderea fișierelor, eliminarea blocărilor, finalizarea animațiilor
  • Definirea variabilelor: defer vede variabilele la momentul ieșirii, nu la momentul declarării
  • Nu înlocuiește do-catch pentru gestionarea erorilor — defer este responsabil pentru cleanup, nu pentru controlul fluxului

Ce este defer?

defer — este o construcție de control a limbajului Swift, introdusă în Swift 2.0 (2015), care amână executarea blocului său până la momentul încheierii scope-ului curent. Caracteristica cheie: defer garantează executarea corpului său indiferent de modul exact de terminare a scope-ului — cu succes (return), cu eroare (throw), prematur (break, continue) sau fatal (fatalError, precondition).

Sintactic defer arată ca defer { /* cod */ } și poate fi plasat oriunde în interiorul scope-ului. Compilatorul Swift garantează că codul din interiorul defer va fi executat, chiar dacă între declararea defer și sfârșitul scope-ului apare o excepție sau return. Acest lucru deosebește fundamental defer de codul obișnuit plasat la sfârșitul funcției, care poate fi omis la ieșirea prematură.

Conform articolului lui Chris Lattner (Creatorul Swift, 2015), defer a fost inspirat de construcții analogice din alte limbaje — defer în Go, finally în Java/Python, scope guard în C++ — dar cu o diferență importantă: în Swift defer se execută la sfârșitul scope-ului, nu imediat după blocul try-catch. Acest lucru oferă un comportament mai predictibil pentru cleanup în funcții cu multiple puncte de ieșire.

Utilizați defer pentru gestionarea simetrică a resurselor: deschiderea fișierului → defer { close }, setarea blocării → defer { unlock }. Un astfel de model garantează că eliberarea resursei nu va fi omisă în nicio circumstanță.

Ordinea de executare a mai multor defer

Când în același scope sunt declarate mai multe defer, ele se execută în ordinea inversă a declarării (LIFO — Last In, First Out). Aceasta înseamnă că ultimul defer declarat se va executa primul, iar primul — ultimul:

swift
func exampleDeferOrder() {
    defer { print("Primul defer") }
    defer { print("Al doilea defer") }
    defer { print("Al treilea defer") }

    print("Corpul funcției")
}
// Rezultat:
// Corpul funcției
// Al treilea defer
// Al doilea defer
// Primul defer

Ordinea LIFO este importantă pentru gestionarea corectă a resurselor imbricate. Dacă mai întâi se deschide fișierul A, apoi fișierul B, ele trebuie eliberate în ordine inversă: mai întâi B, apoi A. Cu defer acest lucru se întâmplă automat — declarați defer imediat după deschiderea fiecărei resurse, iar ordinea de curățare va fi corectă indiferent de numărul punctelor de ieșire din funcție.

Conform Swift by Sundell (2024), această caracteristică face defer ideal pentru blocări imbricate și tranzacții: capturarea blocării → defer { unlock } → capturarea următoarei → defer { unlock }. LIFO garantează că blocările sunt eliberate în ordinea inversă capturării, prevenind blocajele.

Defer pentru curățarea resurselor

Aplicarea principală a defer — curățarea garantată a resurselor. Să luăm în considerare lucrul cu sistemul de fișiere. Deschiderea unui fișier prin FileHandle necesită închiderea explicită — defer garantează că close va fi apelat în orice scenariu:

swift
func readFile(path: String) throws -> String {
    let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path))
    defer { try? handle.close() }

    let data = try handle.readToEnd()
    guard let data else { throw FileError.empty() }

    return String(data: data, encoding: .utf8) ?? ""
    // handle.close() va fi apelat chiar și la throw sau return
}

Un alt scenariu tipic — animațiile UI cu un steag de încărcare. Înainte de începerea încărcării se setează steagul isLoading = true, iar defer îl schimbă la false la ieșirea din funcție, indiferent de succesul sau eroarea cererii. Aceasta previne situația în care steagul rămâne true din cauza unei erori netratate și blochează interfața pentru totdeauna.

Conform Bitbucket Engineering Blog (2024), defer este utilizat și pentru profilare: la începutul funcției se poate înregistra timpul, iar în defer — calcula și afișa diferența. Aceasta oferă măsurători precise ale performanței tuturor căilor de execuție, inclusiv a celor eronate.

Defer și gestionarea erorilor

defer se combină eficient cu funcțiile throws. Când o funcție poate arunca o eroare în orice etapă, defer garantează cleanup fără duplicarea codului în fiecare bloc catch sau ieșire timpurie guard:

swift
func processTransaction() throws {
    let db = try openDatabase()
    defer { closeDatabase(db) }

    let user = try fetchUser(from: db)
    defer { logAudit(user) }

    let result = try performPayment(user)
    sendNotification(result)
    // closeDatabase(db) și logAudit(user) vor fi apelate
    // la orice throw sau return
}

Important: defer se execută înainte de transferul controlului din blocul catch, dar după apariția erorii. Dacă în defer este aruncată o eroare, Swift nu permite utilizarea directă a try în interiorul defer — este necesar try? sau try!. Conform Apple Documentation, Swift nu permite ca o eroare să „iasă" din defer, deoarece aceasta ar încălca garanția de execuție a blocului.

Plasați defer imediat după capturarea resursei. Aceasta respectă principiul proximității: cititorul vede capturarea și eliberarea una lângă alta, ceea ce crește fiabilitatea codului și simplifică revizuirea codului.

Reguli ale domeniului de vizibilitate

defer se execută la ieșirea din scope-ul în care a fost declarat. Dacă defer este declarat în interiorul unui bloc do, se execută la ieșirea din acest bloc, nu din funcția exterioară. Dacă este în interiorul unei bucle for — la fiecare iterație:

swift
func scopeExample() {
    print("start")
    do {
        defer { print("defer în bloc do") }
        print("inside do")
    }
    // "defer în bloc do" afișează aici
    print("after do")
}
    // Rezultat: start, inside do, defer în bloc do, after do

for i in 1...3 {
    defer { print("end iteration \(i)") }
    print("iteration \(i)")
}
    // Rezultat: iterația 1, sfârșitul iterației 1, iterația 2, sfârșitul iterației 2, ...

Variabilele capturate de defer sunt citite la momentul ieșirii din scope, nu la momentul declarării defer. Dacă o variabilă se modifică între declararea defer și sfârșitul scope-ului, defer va vedea ultima valoare. Aceasta este o diferență importantă față de closures, unde capturarea are loc la momentul creării. Fiți atenți: modificările variabilei după declararea defer vor afecta executarea acestuia.

Greșeli tipice cu defer

Prima greșeală — presupunerea unei ordini de executare diferite de LIFO. Dacă ordinea cleanup este importantă, iar defer-urile sunt declarate într-o ordine incorectă, resursele pot fi eliberate cu încălcarea dependențelor. Soluție: declarați defer imediat după capturarea fiecărei resurse. A doua resursă deschisă → defer { close second } înainte ca prima să fie închisă.

A doua greșeală — utilizarea defer pentru logică nelegată de curățare. defer este destinat pentru cleanup garantat, nu pentru fluxul principal de control. Dacă codul în defer afectează valoarea returnată, aceasta este aproape întotdeauna o greșeală. defer nu poate modifica valoarea de return a funcției (spre deosebire de Java finally, unde return în finally suprascrie return-ul original).

A treia greșeală — aruncarea unei erori din defer. Swift interzice try în interiorul defer dacă eroarea s-ar putea propaga în exterior. Utilizați try? sau try! pentru operațiile care pot arunca o eroare sau înfășurați-le într-o funcție separată fără throws. Conform O'Reilly „Swift in Depth" (2025), o bună practică este să faceți funcțiile de cleanup ne-aruncătoare (non-throwing) sau să gestionați erorile în interiorul defer.

Întrebări frecvente

Ce este defer în Swift?

defer — o construcție de control Swift care amână executarea blocului până la ieșirea din domeniul de vizibilitate curent. Blocul se execută întotdeauna — la return, throw, break sau terminare normală. Utilizat pentru curățarea garantată a resurselor: închiderea fișierelor, eliminarea blocărilor.

În ce ordine se execută mai multe defer?

În ordinea inversă a declarării (LIFO) — ultimul defer declarat se execută primul. Aceasta garantează un cleanup corect al resurselor imbricate: dacă resursa B a fost deschisă după A, va fi închisă înainte de A, prevenind dependențele de resurse deja eliberate.

Se poate arunca o eroare din defer?

Nu direct — Swift interzice propagarea erorii din defer. Utilizați try? sau try! pentru operațiile care pot arunca o eroare. Cea mai bună practică este să faceți funcțiile de cleanup non-throwing sau să gestionați erorile în interiorul defer fără propagare în exterior.

Care este diferența dintre defer și do-catch-finally?

defer este legat de scope și se execută la orice ieșire, inclusiv return, throw și break. finally (în alte limbaje) este legat de try-catch și se execută doar la prezența try. În Swift nu există finally — defer acoperă complet acest scenariu și funcționează pentru orice scope, nu doar pentru gestionarea erorilor.

Vede defer modificările variabilelor după declararea sa?

Da, defer citește variabilele la momentul ieșirii din scope, nu la momentul declarării. Dacă o variabilă se modifică după declararea defer, blocul defer va vedea ultima valoare. Aceasta diferă de closures obișnuite, unde capturarea este fixată la momentul creării.

Rezumat

  • defer — bloc de finalizare executat la ieșirea din scope indiferent de motiv (return, throw, break, terminare normală)
  • Ordinea LIFO — mai multe defer se execută de jos în sus, ultimul declarat — primul executat
  • Curățarea resurselor — aplicarea principală: închiderea fișierelor, eliminarea blocărilor, eliberarea pointerilor, oprirea animațiilor
  • Compatibilitatea cu throws: defer se execută după eroare, dar înainte de ieșirea din blocul catch; eroarea din defer nu se propagă
  • Scope: defer se execută la ieșirea din scope-ul în care a fost declarat — bloc do, buclă, funcție
  • Capturarea variabilelor: defer citește valorile la momentul ieșirii, nu la momentul declarării — fiți atenți la mutații
  • Bună practică: declarați defer imediat după capturarea resursei, nu utilizați defer pentru logică de afaceri, faceți funcțiile de cleanup non-throwing

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