defer — шта је то, синтакса и редослед извршења

Аутор: IT Sectr Објављено: 2026-06-20 Време читања: 8 мин

defer — управљачка конструкција у Swift-у која планира извршење блока кода за тренутак изласка из текућег опсега видљивости (scope). Блок defer се извршава независно од начина завршетка — return, break, throw, fatalError или нормалан завршетак. Према Swift Language Guide (2025), при присуству више defer-а у истом scope-у, они се извршавају обрнутим редоследом декларисања — последњи декларисани се извршава први (LIFO). Ово чини defer незаменљивим за гарантовано чишћење ресурса: затварање дескриптора датотека, уклањање блокова, ослобађање привремених показивача без ризика да се cleanup пропусти при превременом изласку.

Главно

  • defer — блок кода који се извршава при изласку из опсега видљивости, независно од разлога изласка (return, throw, break)
  • Редослед LIFO: више defer-а се извршава одоздо према горе — последњи декларисани се активира први
  • Чишћење ресурса — основна примена: затварање датотека, уклањање блокова, завршавање анимација
  • Дефиниција променљивих: defer види променљиве у тренутку изласка, а не у тренутку декларисања
  • Не замењује do-catch за обраду грешака — defer је одговоран за cleanup, а не за контролу тока

Шта је defer?

defer — је управљачка конструкција језика Swift, уведена у Swift 2.0 (2015), која одлаже извршење свог блока до тренутка када се текући scope завршава. Кључна карактеристика: defer гарантује извршење свог тела независно од тога како се тачно scope завршава — успешно (return), са грешком (throw), превремено (break, continue) или фатално (fatalError, precondition).

Синтаксно defer изгледа као defer { /* код */ } и може се налазити на било ком месту унутар scope-а. Компајлер Swift гарантује да ће код унутар defer-а бити извршен, чак и ако између декларисања defer-а и краја scope-а дође до изузетка или return-а. Ово суштински разликује defer од обичног кода постављеног на крају функције, који може бити прескочен при превременом изласку.

Према чланку Криса Латера (Творац Swift, 2015), defer је инспирисан аналогним конструкцијама у другим језицима — defer у Go, finally у Java/Python, scope guard у C++ — али са важном разликом: у Swift-у defer се извршава на крају scope-а, а не одмах после try-catch блока. Ово даје предвидљивије понашање за cleanup у функцијама са вишеструким тачкама изласка.

Користите defer за симетрично управљање ресурсима: отварање датотеке → defer { close }, постављање блока → defer { unlock }. Такав образац гарантује да ослобађање ресурса неће бити пропуштено ни под којим околностима.

Редослед извршења више defer-а

Када је у истом scope-у декларисано више defer-а, они се извршавају обрнутим редоследом декларисања (LIFO — Last In, First Out). То значи да ће се последњи декларисани defer извршити први, а први — последњи:

swift
func exampleDeferOrder() {
    defer { print("Први defer") }
    defer { print("Други defer") }
    defer { print("Трећи defer") }

    print("Тело функције")
}
// Резултат:
// Тело функције
// Трећи defer
// Други defer
// Први defer

Редослед LIFO је важан за коректно управљање угњежденим ресурсима. Ако се прво отвара датотека А, затим датотека Б, потребно их је ослободити обрнутим редоследом: прво Б, па А. Са defer-ом се то дешава аутоматски — декларишите defer одмах након отварања сваког ресурса, и редослед чишћења ће бити исправан без обзира на број тачака изласка из функције.

Према Swift by Sundell (2024), ова карактеристика чини defer идеалним за угњеждене блокове и трансакције: захватање блока → defer { unlock } → захватање следећег → defer { unlock }. LIFO гарантује да се блокови ослобађају обрнутим редоследом од захватања, спречавајући мртве блокове.

Defer за чишћење ресурса

Основна примена defer-а — гарантовано чишћење ресурса. Размотримо рад са датотечним системом. Отварање датотеке путем FileHandle захтева експлицитно затварање — defer гарантује да ће close бити позван у сваком сценарију:

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() ће бити позван чак и при throw или return
}

Други типичан сценарио — UI-анимације са заставицом учитавања. Пре почетка учитавања поставља се заставица isLoading = true, а defer је мења у false при изласку из функције, независно од успеха или грешке захтева. Ово спречава ситуацију у којој заставица остаје true због необрађене грешке и блокира интерфејс заувек.

Према Bitbucket Engineering Blog (2024), defer се такође користи за профилисање: на почетку функције може се забележити време, а у defer-у — израчунати и приказати разлика. Ово даје прецизна мерења перформанси свих путева извршења, укључујући и оне са грешкама.

Defer и обрада грешака

defer се ефикасно комбинује са throws-функцијама. Када функција може да баци грешку у било којој фази, defer гарантује cleanup без дуплирања кода у сваком catch блоку или 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) и logAudit(user) ће бити позвани
    // при сваком throw или return
}

Важно: defer се извршава пре преноса контроле из catch блока, али након настанка грешке. Ако је у defer-у бачена грешка, Swift не дозвољава коришћење try унутар defer-а директно — потребно је try? или try!. Према Apple Documentation, Swift не дозвољава да грешка „излети" из defer-а, јер би то прекршило гаранцију извршења блока.

Поставите defer одмах након захватања ресурса. Ово следи принцип близине: читалац види захватање и ослобађање једно поред другог, што повећава поузданост кода и поједностављује преглед кода.

Правила опсега видљивости

defer се извршава при изласку из scope-а у коме је декларисан. Ако је defer декларисан унутар do-блока, извршава се при изласку из тог блока, а не из спољашње функције. Ако је унутар for-петље — при свакој итерацији:

swift
func scopeExample() {
    print("start")
    do {
        defer { print("defer у do-блоку") }
        print("inside do")
    }
    // "defer у do-блоку" исписује овде
    print("after do")
}
    // Резултат: start, inside do, defer у do-блоку, after do

for i in 1...3 {
    defer { print("end iteration \(i)") }
    print("iteration \(i)")
}
    // Резултат: итерација 1, крај итерације 1, итерација 2, крај итерације 2, ...

Променљиве које је defer захватио читају се у тренутку изласка из scope-а, а не у тренутку декларисања defer-а. Ако се променљива мења између декларисања defer-а и краја scope-а, defer ће видети последњу вредност. Ово је важна разлика у односу на затворења (closures), где се захватање дешава у тренутку креирања. Будите пажљиви: промене променљиве након декларисања defer-а ће утицати на његово извршење.

Типичне грешке са defer-ом

Прва грешка — претпоставка о редоследу извршења различитом од LIFO. Ако је редослед cleanup-а важан, а defer-и су декларисани у погрешном редоследу, ресурси се могу ослобађати уз нарушавање зависности. Решење: декларишите defer одмах након захватања сваког ресурса. Други ресурс отворен → defer { close second } пре него што је први затворен.

Друга грешка — коришћење defer-а за логику која није везана за чишћење. defer је намењен за гарантовани cleanup, а не за главни ток контроле. Ако код у defer-у утиче на повратну вредност, то је готово увек грешка. defer не може да промени return вредност функције (за разлику од Java finally, где return у finally преписује оригинални return).

Трећа грешка — бацање грешке из defer-а. Swift забрањује try унутар defer-а ако грешка може да се прошири напоље. Користите try? или try! за операције које могу да баце грешку, или их умотајте у посебну функцију без throws. Према O'Reilly „Swift in Depth" (2025), добра пракса је да cleanup функције буду не-бацајуће (non-throwing) или да се грешке обрађују унутар defer-а.

Често постављана питања

Шта је defer у Swift-у?

defer — управљачка конструкција Swift-а која одлаже извршење блока до изласка из текућег опсега видљивости. Блок се увек извршава — при return, throw, break или нормалном завршетку. Користи се за гарантовано чишћење ресурса: затварање датотека, уклањање блокова.

Којим редоследом се извршава више defer-а?

Обрнутим редоследом декларисања (LIFO) — последњи декларисани defer се извршава први. Ово гарантује коректан cleanup угњеждених ресурса: ако је ресурс Б отворен после А, биће затворен пре А, што спречава зависности од већ ослобођених ресурса.

Може ли се бацити грешка из defer-а?

Не директно — Swift забрањује пропагирање грешке из defer-а. Користите try? или try! за операције које могу да баце грешку. Најбоља пракса је да cleanup функције буду non-throwing или да се грешке обрађују унутар defer-а без пропагирања напоље.

Која је разлика између defer-а и do-catch-finally?

defer је везан за scope и извршава се при сваком изласку, укључујући return, throw и break. finally (у другим језицима) је везан за try-catch и извршава се само при постојању try-а. У Swift-у не постоји finally — defer у потпуности покрива овај сценариј и ради за било који scope, а не само за обраду грешака.

Да ли defer види измене променљивих након свог декларисања?

Да, defer чита променљиве у тренутку изласка из scope-а, а не у тренутку декларисања. Ако се променљива мења након декларисања defer-а, блок defer ће видети последњу вредност. Ово се разликује од обичних затворења, где се захватање фиксира у тренутку креирања.

Закључак

  • defer — блок финализације који се извршава при изласку из scope-а независно од разлога (return, throw, break, нормалан завршетак)
  • LIFO редослед — више defer-а се извршава одоздо према горе, последњи декларисани — први извршени
  • Чишћење ресурса — основна примена: затварање датотека, уклањање блокова, ослобађање показивача, заустављање анимација
  • Компатибилност са throws: defer се извршава након грешке, али пре изласка из catch блока; грешка из defer-а се не пропагира
  • Scope: defer се извршава при изласку из scope-а у коме је декларисан — do-блок, петља, функција
  • Захватање променљивих: defer чита вредности у тренутку изласка, а не у тренутку декларисања — будите пажљиви са мутацијама
  • Најбоља пракса: декларишите defer одмах након захватања ресурса, не користите defer за пословну логику, правите cleanup функције non-throwing

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође