defer — vad är det, syntax och utförandeordning

Författare: IT Sectr Publicerad: 2026-06-20 Lästid: 8 min

defer — en kontrollkonstruktion i Swift som schemalägger exekveringen av ett kodblock till det ögonblick då det aktuella omfånget (scope) lämnas. Defer-blocket exekveras oavsett hur avslutningen sker — return, break, throw, fatalError eller normal avslutning. Enligt Swift Language Guide (2025) utförs flera defer i samma scope i omvänd deklarationsordning — den senast deklarerade exekveras först (LIFO). Detta gör defer oumbärlig för garanterad resursrengöring: stängning av filbeskrivare, borttagning av lås, frigöring av temporära pekare utan risk att missa cleanup vid förtida utträde.

Huvudpunkter

  • defer — kodblock som exekveras när omfånget lämnas, oavsett orsak till utträde (return, throw, break)
  • Ordning LIFO: flera defer exekveras nerifrån och upp — den senast deklarerade aktiveras först
  • Resursrengöring — huvudsaklig tillämpning: stängning av filer, borttagning av lås, avslutning av animationer
  • Definition av variabler: defer ser variabler vid utträdesögonblicket, inte vid deklarationsögonblicket
  • Ersätter inte do-catch för felhantering — defer ansvarar för cleanup, inte för flödeskontroll

Vad är defer?

defer — är en kontrollkonstruktion i Swift, introducerad i Swift 2.0 (2015), som fördröjer exekveringen av sitt block tills det aktuella scopet avslutas. Nyckelegenskap: defer garanterar exekvering av sin kropp oavsett hur scopet exakt avslutas — framgångsrikt (return), med fel (throw), förtida (break, continue) eller fatalt (fatalError, precondition).

Syntaxmässigt ser defer ut som defer { /* kod */ } och kan placeras var som helst inom scopet. Swift-kompilatorn garanterar att koden inuti defer exekveras, även om ett undantag eller return inträffar mellan deklarationen av defer och slutet av scopet. Detta skiljer defer fundamentalt från vanlig kod placerad i slutet av en funktion, som kan hoppas över vid förtida utträde.

Enligt en artikel av Chris Lattner (Swift-skaparen, 2015) inspirerades defer av liknande konstruktioner i andra språk — defer i Go, finally i Java/Python, scope guard i C++ — men med en viktig skillnad: i Swift exekveras defer i slutet av scopet, inte omedelbart efter ett try-catch-block. Detta ger mer förutsägbart beteende för cleanup i funktioner med flera utgångspunkter.

Använd defer för symmetrisk resurshantering: öppna fil → defer { close }, ställ in lås → defer { unlock }. Ett sådant mönster garanterar att resursfrigöring inte missas under några omständigheter.

Exekveringsordning för flera defer

När flera defer deklareras i samma scope exekveras de i omvänd ordning av deklaration (LIFO — Last In, First Out). Det innebär att den senast deklarerade defer exekveras först, och den första — sist:

swift
func exampleDeferOrder() {
    defer { print("Första defer") }
    defer { print("Andra defer") }
    defer { print("Tredje defer") }

    print("Funktionskropp")
}
// Utdata:
// Funktionskropp
// Tredje defer
// Andra defer
// Första defer

LIFO-ordningen är viktig för korrekt hantering av nästlade resurser. Om fil A öppnas först, sedan fil B, måste de frigöras i omvänd ordning: först B, sedan A. Med defer sker detta automatiskt — deklarera defer omedelbart efter att varje resurs öppnats, och rengöringsordningen blir korrekt oavsett antalet utgångspunkter från funktionen.

Enligt Swift by Sundell (2024) gör denna egenskap defer idealisk för nästlade lås och transaktioner: lås tagning → defer { unlock } → tagning av nästa → defer { unlock }. LIFO garanterar att lås frigörs i omvänd ordning av tagning, vilket förhindrar deadlocks.

Defer för resursrengöring

Huvudsaklig tillämpning av defer — garanterad resursrengöring. Låt oss överväga arbete med filsystemet. Att öppna en fil via FileHandle kräver explicit stängning — defer garanterar att close anropas i varje scenario:

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() kommer att anropas även vid throw eller return
}

Ett annat typiskt scenario — UI-animationer med en laddningsflagga. Innan laddning påbörjas ställs flaggan isLoading = true in, och defer ändrar den till false vid utträde från funktionen, oavsett om begäran lyckas eller misslyckas. Detta förhindrar situationen där flaggan förblir true på grund av ett ohanterat fel och blockerar gränssnittet för alltid.

Enligt Bitbucket Engineering Blog (2024) används defer även för profilering: i början av en funktion kan tiden registreras, och i defer — skillnaden beräknas och visas. Detta ger noggranna prestandamätningar av alla exekveringsvägar, inklusive felaktiga.

Defer och felhantering

defer kombineras effektivt med throws-funktioner. När en funktion kan kasta ett fel i vilket skede som helst, garanterar defer cleanup utan att duplicera kod i varje catch-block eller guard-förtida utträde:

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) och logAudit(user) kommer att anropas
    // vid varje throw eller return
}

Viktigt: defer exekveras före överföring av kontroll från catch-blocket, men efter att felet inträffat. Om ett fel kastas i defer tillåter Swift inte direkt användning av try inuti defer — try? eller try! krävs. Enligt Apple Documentation tillåter Swift inte att ett fel „flyr" från defer, eftersom det skulle bryta blockets exekveringsgaranti.

Placera defer omedelbart efter resurs tagning. Detta följer närhetsprincipen: läsaren ser tagning och frigöring bredvid varandra, vilket ökar kodens tillförlitlighet och förenklar kodgranskning.

Regler för omfång

defer exekveras när det scope där det deklarerades lämnas. Om defer deklareras inuti ett do-block exekveras det när detta block lämnas, inte från den yttre funktionen. Om inuti en for-loop — vid varje iteration:

swift
func scopeExample() {
    print("start")
    do {
        defer { print("do-block defer") }
        print("inside do")
    }
    // "do-block defer" skriver ut här
    print("after do")
}
    // Utdata: start, inside do, do-block defer, after do

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

Variabler som fångats av defer läses vid utträdesögonblicket från scopet, inte vid deklarationsögonblicket för defer. Om en variabel ändras mellan deklarationen av defer och slutet av scopet kommer defer att se det sista värdet. Detta är en viktig skillnad från closures, där fångst sker vid skapelseögonblicket. Var uppmärksam: ändringar av en variabel efter deklarationen av defer kommer att påverka dess exekvering.

Vanliga misstag med defer

Första misstaget — antagande om annan exekveringsordning än LIFO. Om cleanup-ordningen är viktig och defer har deklarerats i fel ordning kan resurser frigöras med beroendebrott. Lösning: deklarera defer omedelbart efter tagning av varje resurs. Andra resursen öppnad → defer { close second } innan den första stängs.

Andra misstaget — användning av defer för logik som inte är relaterad till rengöring. defer är avsett för garanterad cleanup, inte för huvudkontrollflödet. Om koden i defer påverkar returvärdet är det nästan alltid ett misstag. defer kan inte ändra en funktions return-värde (till skillnad från Java finally, där return i finally skriver över den ursprungliga return).

Tredje misstaget — kasta ett fel från defer. Swift förbjuder try inuti defer om felet kan spridas utåt. Använd try? eller try! för operationer som kan kasta ett fel, eller slå in dem i en separat funktion utan throws. Enligt O'Reilly „Swift in Depth" (2025) är god praxis att göra cleanup-funktioner non-throwing eller hantera fel inuti defer.

Vanliga frågor

Vad är defer i Swift?

defer — en Swift-kontrollkonstruktion som fördröjer exekveringen av ett block tills det aktuella omfånget lämnas. Blocket exekveras alltid — vid return, throw, break eller normal avslutning. Används för garanterad resursrengöring: stängning av filer, borttagning av lås.

I vilken ordning exekveras flera defer?

I omvänd deklarationsordning (LIFO) — den senast deklarerade defer exekveras först. Detta garanterar korrekt cleanup av nästlade resurser: om resurs B öppnades efter A kommer den att stängas före A, vilket förhindrar beroenden av redan frigjorda resurser.

Kan man kasta ett fel från defer?

Inte direkt — Swift förbjuder spridning av fel från defer. Använd try? eller try! för operationer som kan kasta fel. Bästa praxis är att göra cleanup-funktioner non-throwing eller hantera fel inuti defer utan att sprida utåt.

Vad är skillnaden mellan defer och do-catch-finally?

defer är bundet till scopet och exekveras vid varje utträde, inklusive return, throw och break. finally (i andra språk) är bundet till try-catch och exekveras endast vid förekomst av try. I Swift finns inget finally — defer täcker helt detta scenario och fungerar för alla scope, inte bara för felhantering.

Ser defer ändringar av variabler efter sin deklaration?

Ja, defer läser variabler vid utträdesögonblicket från scopet, inte vid deklarationsögonblicket. Om en variabel ändras efter deklarationen av defer kommer defer-blocket att se det sista värdet. Detta skiljer sig från vanliga closures, där fångst fastställs vid skapelseögonblicket.

Sammanfattning

  • defer — finaliseringsblock som exekveras när scopet lämnas oavsett orsak (return, throw, break, normal avslutning)
  • LIFO-ordning — flera defer exekveras nerifrån och upp, senast deklarerad — först exekverad
  • Resursrengöring — huvudsaklig tillämpning: stängning av filer, borttagning av lås, frigöring av pekare, stoppning av animationer
  • Kompatibilitet med throws: defer exekveras efter fel men före utträde från catch-block; fel från defer sprids inte
  • Scope: defer exekveras när scopet där det deklarerades lämnas — do-block, loop, funktion
  • Variabelfångst: defer läser värden vid utträdesögonblicket, inte vid deklarationsögonblicket — var uppmärksam på mutationer
  • Bästa praxis: deklarera defer omedelbart efter resurstagning, använd inte defer för affärslogik, gör cleanup-funktioner non-throwing

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också