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 — ä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.
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:
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.
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:
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 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:
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.
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:
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.
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
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 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.
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.
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.
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
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.
Läs också