defer — řídicí konstrukce ve Swift, která plánuje provedení bloku kódu na okamžik opuštění aktuálního rozsahu viditelnosti (scope). Blok defer se provádí nezávisle na způsobu ukončení — return, break, throw, fatalError nebo normální ukončení. Podle Swift Language Guide (2025) se při výskytu více defer v jednom scope provádějí v opačném pořadí deklarace — poslední deklarovaný se provádí jako první (LIFO). To činí defer nenahraditelným pro garantované čištění zdrojů: zavírání souborových deskriptorů, odebírání zámků, uvolňování dočasných ukazatelů bez rizika vynechání cleanup při předčasném opuštění.
Hlavní
defer — je řídicí konstrukce jazyka Swift, představená ve Swift 2.0 (2015), která odkládá provedení svého bloku do okamžiku dokončení aktuálního scope. Klíčová vlastnost: defer garantuje provedení svého těla nezávisle na tom, jak přesně scope končí — úspěšně (return), s chybou (throw), předčasně (break, continue) nebo fatálně (fatalError, precondition).
Syntakticky defer vypadá jako defer { /* kód */ } a může být umístěn kdekoli uvnitř scope. Kompilátor Swift garantuje, že kód uvnitř defer bude proveden, i když mezi deklarací defer a koncem scope dojde k výjimce nebo return. To zásadně odlišuje defer od běžného kódu umístěného na konci funkce, který může být při předčasném opuštění přeskočen.
Podle článku Chrise Lattnera (Tvůrce Swift, 2015) byl defer inspirován analogickými konstrukcemi v jiných jazycích — defer v Go, finally v Java/Python, scope guard v C++ — ale s důležitým rozdílem: ve Swift se defer provádí na konci scope, ne ihned po bloku try-catch. To poskytuje předvídatelnější chování pro cleanup ve funkcích s více body opuštění.
Používejte defer pro symetrické řízení zdrojů: otevření souboru → defer { close }, nastavení zámku → defer { unlock }. Takový vzor garantuje, že uvolnění zdroje nebude za žádných okolností vynecháno.
Když je v jednom scope deklarováno několik defer, provádějí se v opačném pořadí deklarace (LIFO — Last In, First Out). To znamená, že poslední deklarovaný defer se provede první, a první — poslední:
func exampleDeferOrder() {
defer { print("První defer") }
defer { print("Druhý defer") }
defer { print("Třetí defer") }
print("Tělo funkce")
}
// Výstup:
// Tělo funkce
// Třetí defer
// Druhý defer
// První defer
Pořadí LIFO je důležité pro správné řízení vnořených zdrojů. Pokud se nejprve otevře soubor A, poté soubor B, je třeba je uvolnit v opačném pořadí: nejprve B, pak A. S defer se to děje automaticky — deklarujte defer ihned po otevření každého zdroje a pořadí čištění bude správné bez ohledu na počet bodů opuštění funkce.
Podle Swift by Sundell (2024) tato vlastnost činí defer ideálním pro vnořené zámky a transakce: zachycení zámku → defer { unlock } → zachycení dalšího → defer { unlock }. LIFO garantuje, že zámky se uvolňují v opačném pořadí zachycení, čímž se předchází zablokování.
Hlavní použití defer — garantované čištění zdrojů Uvažme práci se souborovým systémem. Otevření souboru pomocí FileHandle vyžaduje explicitní zavření — defer garantuje, že close bude zavolán v každém scénáři:
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() bude zavolán i při throw nebo return
}
Další typický scénář — UI-animace s příznakem načítání. Před zahájením načítání se nastaví příznak isLoading = true a defer jej při opuštění funkce změní na false, bez ohledu na úspěch nebo chybu požadavku. Tím se předchází situaci, kdy příznak zůstává true kvůli neošetřené chybě a blokuje rozhraní navždy.
Podle Bitbucket Engineering Blog (2024) se defer také používá pro profilování: na začátku funkce lze zaznamenat čas a v defer — vypočítat a zobrazit rozdíl. To poskytuje přesná měření výkonu všech cest provádění, včetně chybových.
defer se efektivně kombinuje s funkcemi throws. Když funkce může vyhodit chybu v jakékoli fázi, defer garantuje cleanup bez duplikování kódu v každém bloku catch nebo předčasném opuštění guard:
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) a logAudit(user) budou zavolány
// při každém throw nebo return
}
Důležité: defer se provádí před předáním řízení z bloku catch, ale po výskytu chyby. Pokud je v defer vyhozena chyba, Swift nepovoluje použití try uvnitř defer přímo — je vyžadováno try? nebo try!. Podle Apple Documentation Swift nedovoluje, aby chyba „vyletěla" z defer, protože by to porušilo záruku provedení bloku.
Umístěte defer ihned po zachycení zdroje. To následuje princip blízkosti: čtenář vidí zachycení a uvolnění vedle sebe, což zvyšuje spolehlivost kódu a zjednodušuje kontrolu kódu.
defer se provádí při opuštění scope, ve kterém byl deklarován. Pokud je defer deklarován uvnitř bloku do, provádí se při opuštění tohoto bloku, ne z vnější funkce. Pokud je uvnitř cyklu for — při každé iteraci:
func scopeExample() {
print("start")
do {
defer { print("defer v do-bloku") }
print("inside do")
}
// "defer v do-bloku" vypisuje zde
print("after do")
}
// Výstup: start, inside do, defer v do-bloku, after do
for i in 1...3 {
defer { print("end iteration \(i)") }
print("iteration \(i)")
}
// Výstup: iterace 1, konec iterace 1, iterace 2, konec iterace 2, ...
Proměnné zachycené deferem se čtou v okamžiku opuštění scope, ne v okamžiku deklarace defer. Pokud se proměnná změní mezi deklarací defer a koncem scope, defer uvidí poslední hodnotu. To je důležitý rozdíl oproti uzávěrům (closures), kde k zachycení dochází v okamžiku vytvoření. Buďte opatrní: změny proměnné po deklaraci defer ovlivní jeho provedení.
První chyba — předpoklad o pořadí provádění odlišném od LIFO. Pokud je pořadí cleanup důležité a defer jsou deklarovány ve špatném pořadí, zdroje mohou být uvolňovány s porušením závislostí. Řešení: deklarujte defer ihned po zachycení každého zdroje. Druhý zdroj otevřen → defer { close second } před uzavřením prvního.
Druhá chyba — použití defer pro logiku nesouvisející s čištěním. defer je určen pro garantovaný cleanup, ne pro hlavní tok řízení. Pokud kód v defer ovlivňuje návratovou hodnotu, je to téměř vždy chyba. defer nemůže změnit return hodnotu funkce (na rozdíl od Java finally, kde return v finally přepisuje původní return).
Třetí chyba — vyhození chyby z defer. Swift zakazuje try uvnitř defer, pokud by se chyba mohla šířit ven. Použijte try? nebo try! pro operace, které mohou vyhodit chybu, nebo je zabalte do samostatné funkce bez throws. Podle O'Reilly „Swift in Depth" (2025) je dobrou praxí učinit cleanup funkce nevyhazující (non-throwing) nebo zpracovávat chyby uvnitř defer.
Často kladené otázky
defer — řídicí konstrukce Swift, která odkládá provedení bloku do okamžiku opuštění aktuálního rozsahu viditelnosti. Blok se provádí vždy — při return, throw, break nebo normálním ukončení. Používá se pro garantované čištění zdrojů: zavírání souborů, odebírání zámků.
V opačném pořadí deklarace (LIFO) — poslední deklarovaný defer se provádí jako první. To garantuje správný cleanup vnořených zdrojů: pokud byl zdroj B otevřen po A, bude uzavřen před A, čímž se předchází závislostem na již uvolněných zdrojích.
Ne přímo — Swift zakazuje šíření chyby z defer. Použijte try? nebo try! pro operace, které mohou vyhodit chybu. Nejlepší praxí je učinit cleanup funkce non-throwing nebo zpracovávat chyby uvnitř defer bez šíření ven.
defer je vázán na scope a provádí se při každém opuštění, včetně return, throw a break. finally (v jiných jazycích) je vázán na try-catch a provádí se pouze při přítomnosti try. Ve Swift není finally — defer plně pokrývá tento scénář a funguje pro jakýkoli scope, nejen pro zpracování chyb.
Ano, defer čte proměnné v okamžiku opuštění scope, ne v okamžiku deklarace. Pokud se proměnná změní po deklaraci defer, blok defer uvidí poslední hodnotu. To se liší od běžných uzávěrů, kde je zachycení fixováno v okamžiku vytvoření.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také