defer — wat is het, syntax en uitvoeringsvolgorde

Auteur: IT Sectr Gepubliceerd: 2026-06-20 Leestijd: 8 min

defer — een controlestructuur in Swift die de uitvoering van een codeblok plant op het moment van verlaten van het huidige bereik (scope). Het defer-blok wordt uitgevoerd ongeacht de manier van afsluiten — return, break, throw, fatalError of normale beëindiging. Volgens de Swift Language Guide (2025) worden bij meerdere defer in één scope deze in omgekeerde declaratievolgorde uitgevoerd — de laatst gedeclareerde wordt als eerste uitgevoerd (LIFO). Dit maakt defer onmisbaar voor gegarandeerde opschoning van resources: het sluiten van bestandsdescriptors, het opheffen van vergrendelingen, het vrijgeven van tijdelijke pointers zonder het risico cleanup te missen bij vroegtijdige afsluiting.

Belangrijkste

  • defer — codeblok dat wordt uitgevoerd bij het verlaten van het bereik, ongeacht de reden van uitgang (return, throw, break)
  • Volgorde LIFO: meerdere defer worden van onder naar boven uitgevoerd — de laatst gedeclareerde wordt als eerste geactiveerd
  • Resource opschoning — belangrijkste toepassing: bestanden sluiten, vergrendelingen opheffen, animaties beëindigen
  • Definitie van variabelen: defer ziet variabelen op het moment van uitgang, niet op het moment van declaratie
  • Vervangt niet do-catch voor foutafhandeling — defer is verantwoordelijk voor cleanup, niet voor stroomcontrole

Wat is defer?

defer — is een controlestructuur van de taal Swift, geïntroduceerd in Swift 2.0 (2015), die de uitvoering van zijn blok uitstelt tot het moment dat het huidige scope wordt beëindigd. Belangrijkste kenmerk: defer garandeert de uitvoering van zijn body ongeacht hoe het scope precies wordt beëindigd — succesvol (return), met fout (throw), voortijdig (break, continue) of fataal (fatalError, precondition).

Syntaxisch ziet defer eruit als defer { /* code */ } en kan overal in het scope worden geplaatst. De Swift-compiler garandeert dat de code binnen defer wordt uitgevoerd, zelfs als er tussen de declaratie van defer en het einde van het scope een uitzondering of return optreedt. Dit onderscheidt defer fundamenteel van gewone code aan het einde van een functie, die kan worden overgeslagen bij vroegtijdige afsluiting.

Volgens een artikel van Chris Lattner (Swift Creator, 2015) is defer geïnspireerd op soortgelijke constructies in andere talen — defer in Go, finally in Java/Python, scope guard in C++ — maar met een belangrijk verschil: in Swift wordt defer uitgevoerd aan het einde van het scope, niet direct na een try-catch-blok. Dit geeft voorspelbaarder gedrag voor cleanup in functies met meerdere uitgangspunten.

Gebruik defer voor symmetrisch resourcebeheer: bestand openen → defer { close }, vergrendeling instellen → defer { unlock }. Zo'n patroon garandeert dat het vrijgeven van de resource onder geen enkele omstandigheid wordt overgeslagen.

Uitvoeringsvolgorde van meerdere defer

Wanneer er meerdere defer in één scope zijn gedeclareerd, worden ze in omgekeerde volgorde van declaratie uitgevoerd (LIFO — Last In, First Out). Dit betekent dat de laatst gedeclareerde defer als eerste wordt uitgevoerd en de eerste als laatste:

swift
func exampleDeferOrder() {
    defer { print("Eerste defer") }
    defer { print("Tweede defer") }
    defer { print("Derde defer") }

    print("Functiebody")
}
// Uitvoer:
// Functiebody
// Derde defer
// Tweede defer
// Eerste defer

De LIFO-volgorde is belangrijk voor correct beheer van geneste resources. Als eerst bestand A wordt geopend en daarna bestand B, moeten ze in omgekeerde volgorde worden vrijgegeven: eerst B, dan A. Met defer gebeurt dit automatisch — declareer defer direct na het openen van elke resource en de opschoningsvolgorde is correct, ongeacht het aantal uitgangspunten van de functie.

Volgens Swift by Sundell (2024) maakt deze eigenschap defer ideaal voor geneste vergrendelingen en transacties: vergrendeling verkrijgen → defer { unlock } → volgende verkrijgen → defer { unlock }. LIFO garandeert dat vergrendelingen in omgekeerde volgorde van verkrijging worden vrijgegeven, wat deadlocks voorkomt.

Defer voor resource opschoning

De belangrijkste toepassing van defer — gegarandeerde opschoning van resources. Laten we werken met het bestandssysteem bekijken. Het openen van een bestand via FileHandle vereist expliciet sluiten — defer garandeert dat close in elk scenario wordt aangeroepen:

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() wordt zelfs bij throw of return aangeroepen
}

Een ander typisch scenario — UI-animaties met een laadvlag. Voordat het laden begint wordt de vlag isLoading = true gezet en defer verandert deze naar false bij het verlaten van de functie, ongeacht het succes of de fout van de aanvraag. Dit voorkomt de situatie waarin de vlag true blijft door een onverwerkte fout en de interface permanent blokkeert.

Volgens Bitbucket Engineering Blog (2024) wordt defer ook gebruikt voor profilering: aan het begin van een functie kan de tijd worden geregistreerd en in defer — het verschil worden berekend en weergegeven. Dit geeft nauwkeurige prestaties voor alle uitvoeringspaden, inclusief foutieve.

Defer en foutafhandeling

defer combineert effectief met throws-functies. Wanneer een functie in elke fase een fout kan gooien, garandeert defer cleanup zonder code te dupliceren in elk catch-blok of guard-vroegtijdige uitgang:

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) en logAudit(user) worden aangeroepen
    // bij elke throw of return
}

Belangrijk: defer wordt vóór de overdracht van controle uit het catch-blok uitgevoerd, maar na het optreden van de fout. Als er in defer een fout wordt gegooid, staat Swift niet toe dat try direct binnen defer wordt gebruikt — try? of try! is vereist. Volgens Apple Documentation staat Swift niet toe dat een fout uit defer „ontsnapt", omdat dit de uitvoeringsgarantie van het blok zou schenden.

Plaats defer direct na het verkrijgen van een resource. Dit volgt het principe van nabijheid: de lezer ziet verkrijging en vrijgave naast elkaar, wat de betrouwbaarheid van de code verhoogt en codebeoordeling vereenvoudigt.

Regels voor bereik

defer wordt uitgevoerd bij het verlaten van het scope waarin het is gedeclareerd. Als defer is gedeclareerd binnen een do-blok, wordt het uitgevoerd bij het verlaten van dit blok, niet van de buitenste functie. Als het binnen een for-lus staat — bij elke iteratie:

swift
func scopeExample() {
    print("start")
    do {
        defer { print("do-blok defer") }
        print("inside do")
    }
    // "do-blok defer" print hier
    print("after do")
}
    // Uitvoer: start, inside do, do-blok defer, after do

for i in 1...3 {
    defer { print("end iteration \(i)") }
    print("iteration \(i)")
}
    // Uitvoer: iteratie 1, einde iteratie 1, iteratie 2, einde iteratie 2, ...

Variabelen die door defer zijn vastgelegd, worden gelezen op het moment van verlaten van het scope, niet op het moment van declaratie van defer. Als een variabele verandert tussen de declaratie van defer en het einde van het scope, zal defer de laatste waarde zien. Dit is een belangrijk verschil met closures, waar vastlegging plaatsvindt op het moment van creatie. Wees voorzichtig: wijzigingen van een variabele na de declaratie van defer hebben invloed op de uitvoering ervan.

Veelvoorkomende fouten met defer

Eerste fout — aanname van een andere uitvoeringsvolgorde dan LIFO. Als de opschoningsvolgorde belangrijk is en defer in de verkeerde volgorde zijn gedeclareerd, kunnen resources worden vrijgegeven met schending van afhankelijkheden. Oplossing: declareer defer direct na het verkrijgen van elke resource. Tweede resource geopend → defer { close second } voordat de eerste wordt gesloten.

Tweede fout — gebruik van defer voor logica die geen verband houdt met opschoning. defer is bedoeld voor gegarandeerde cleanup, niet voor de hoofdstroom van controle. Als code in defer de retourwaarde beïnvloedt, is dit bijna altijd een fout. defer kan de return-waarde van een functie niet wijzigen (in tegenstelling tot Java finally, waar return in finally de originele return overschrijft).

Derde fout — een fout gooien uit defer. Swift verbiedt try binnen defer als de fout naar buiten kan worden verspreid. Gebruik try? of try! voor bewerkingen die een fout kunnen gooien, of wikkel ze in een aparte functie zonder throws. Volgens O'Reilly „Swift in Depth" (2025) is het een goede praktijk om cleanup-functies non-throwing te maken of fouten binnen defer af te handelen.

Veelgestelde vragen

Wat is defer in Swift?

defer — een Swift-controlestructuur die de uitvoering van een blok uitstelt tot het verlaten van het huidige bereik. Het blok wordt altijd uitgevoerd — bij return, throw, break of normale beëindiging. Gebruikt voor gegarandeerde opschoning van resources: bestanden sluiten, vergrendelingen opheffen.

In welke volgorde worden meerdere defer uitgevoerd?

In omgekeerde declaratievolgorde (LIFO) — de laatst gedeclareerde defer wordt als eerste uitgevoerd. Dit garandeert correcte opschoning van geneste resources: als resource B is geopend na A, wordt deze gesloten vóór A, wat afhankelijkheden van reeds vrijgegeven resources voorkomt.

Kan er een fout uit defer worden gegooid?

Niet direct — Swift verbiedt het verspreiden van een fout uit defer. Gebruik try? of try! voor bewerkingen die een fout kunnen gooien. De beste praktijk is om cleanup-functies non-throwing te maken of fouten binnen defer af te handelen zonder naar buiten te verspreiden.

Wat is het verschil tussen defer en do-catch-finally?

defer is gebonden aan het scope en wordt uitgevoerd bij elke uitgang, inclusief return, throw en break. finally (in andere talen) is gebonden aan try-catch en wordt alleen uitgevoerd bij aanwezigheid van try. In Swift bestaat geen finally — defer dekt dit scenario volledig en werkt voor elk scope, niet alleen voor foutafhandeling.

Ziet defer wijzigingen van variabelen na zijn declaratie?

Ja, defer leest variabelen op het moment van verlaten van het scope, niet op het moment van declaratie. Als een variabele verandert na de declaratie van defer, zal het defer-blok de laatste waarde zien. Dit verschilt van gewone closures, waar vastlegging wordt bepaald op het moment van creatie.

Samenvatting

  • defer — finalisatieblok dat wordt uitgevoerd bij het verlaten van het scope ongeacht de reden (return, throw, break, normale beëindiging)
  • LIFO-volgorde — meerdere defer worden van onder naar boven uitgevoerd, laatste gedeclareerd — eerste uitgevoerd
  • Resource opschoning — belangrijkste toepassing: bestanden sluiten, vergrendelingen opheffen, pointers vrijgeven, animaties stoppen
  • Compatibiliteit met throws: defer wordt uitgevoerd na fout maar vóór het verlaten van het catch-blok; fout uit defer wordt niet verspreid
  • Scope: defer wordt uitgevoerd bij het verlaten van het scope waarin het is gedeclareerd — do-blok, lus, functie
  • Vastleggen van variabelen: defer leest waarden op het moment van uitgang, niet op het moment van declaratie — let op mutaties
  • Beste praktijk: declareer defer direct na het verkrijgen van de resource, gebruik defer niet voor bedrijfslogica, maak cleanup-functies non-throwing

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.

Bespreek het project

Lees ook