defer — o que é, sintaxe e ordem de execução

Autor: IT Sectr Publicado: 2026-06-20 Tempo de leitura: 8 min

defer é uma construção de controlo de fluxo em Swift que programa a execução de um bloco de código para o momento de saída do âmbito atual. O bloco defer executa independentemente de como o âmbito termina — return, break, throw, fatalError ou conclusão normal. De acordo com o Swift Language Guide (2025), quando vários defers estão presentes num mesmo âmbito, eles executam por ordem inversa à declaração — o último declarado executa primeiro (LIFO). Isto torna o defer indispensável para a limpeza garantida de recursos: fechar descritores de ficheiro, libertar bloqueios, libertar ponteiros temporários sem o risco de perder a limpeza numa saída antecipada.

Pontos-Chave

  • defer — um bloco de código executado ao sair de um âmbito, independentemente do motivo de saída (return, throw, break)
  • Ordem LIFO: vários defers executam de baixo para cima — o último declarado executa primeiro
  • Limpeza de recursos — caso de uso principal: fechar ficheiros, libertar bloqueios, completar animações
  • Avaliação de variáveis: defer vê as variáveis no momento da saída, não no momento da declaração
  • Não substitui do-catch para tratamento de erros — defer trata da limpeza, não do controlo de fluxo

O que é defer?

defer é uma construção de controlo de fluxo em Swift, introduzida no Swift 2.0 (2015), que adia a execução do seu bloco até que o âmbito atual termine. A característica principal: defer garante que o seu corpo executa independentemente de como o âmbito termina — com sucesso (return), com erro (throw), prematuramente (break, continue) ou fatalmente (fatalError, precondition).

Sintaticamente, defer escreve-se como defer { /* código */ } e pode ser colocado em qualquer lugar dentro do âmbito. O compilador Swift garante que o código dentro de defer será executado mesmo que ocorra uma exceção ou return entre a declaração do defer e o fim do âmbito. Isto distingue fundamentalmente o defer do código normal colocado no final de uma função, que pode ser ignorado numa saída antecipada.

De acordo com um artigo de Chris Lattner (Criador do Swift, 2015), o defer foi inspirado por construções semelhantes noutras linguagens — defer em Go, finally em Java/Python, scope guard em C++ — mas com uma diferença importante: em Swift, o defer executa no final do âmbito, não imediatamente após um bloco try-catch. Isto proporciona um comportamento mais previsível para a limpeza em funções com múltiplos pontos de saída.

Use defer para gestão simétrica de recursos: abrir ficheiro → defer { close }, adquirir bloqueio → defer { unlock }. Este padrão garante que a libertação de recursos nunca é perdida em circunstância alguma.

Ordem de execução de vários defers

Quando vários defers são declarados no mesmo âmbito, executam por ordem inversa à declaração (LIFO — Last In, First Out). Isto significa que o último defer declarado executa primeiro, e o primeiro executa último:

swift
func exampleDeferOrder() {
    defer { print("Primeiro defer") }
    defer { print("Segundo defer") }
    defer { print("Terceiro defer") }

    print("Corpo da função")
}
// Saída:
// Corpo da função
// Terceiro defer
// Segundo defer
// Primeiro defer

A ordem LIFO é importante para a gestão correta de recursos aninhados. Se o ficheiro A é aberto primeiro, depois o ficheiro B, devem ser libertados por ordem inversa: primeiro B, depois A. Com defer, isto acontece automaticamente — declare um defer logo após abrir cada recurso, e a ordem de limpeza será correta independentemente do número de pontos de saída da função.

Segundo Swift by Sundell (2024), esta característica torna o defer ideal para bloqueios aninhados e transações: adquirir bloqueio → defer { unlock } → adquirir próximo → defer { unlock }. LIFO garante que os bloqueios são libertados por ordem inversa à aquisição, prevenindo impasses.

Defer para limpeza de recursos

O caso de uso principal do defer é a limpeza garantida de recursos. Considere trabalhar com o sistema de ficheiros. Abrir um ficheiro através de FileHandle requer um fecho explícito — defer garante que close será chamado em qualquer cenário:

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() será chamado mesmo em throw ou return
}

Outro cenário típico são as animações de UI com um indicador de carregamento. Antes de iniciar um carregamento, defina o indicador isLoading = true, e defer volta a colocá-lo em false ao sair da função, independentemente do sucesso ou falha do pedido. Isto impede que o indicador permaneça true devido a um erro não tratado, bloqueando a interface para sempre.

De acordo com o Bitbucket Engineering Blog (2024), o defer também é usado para perfilação: registe o tempo no início de uma função, e em defer — calcule e mostre a diferença. Isto dá medições precisas de desempenho para todos os caminhos de execução, incluindo os erróneos.

Defer e tratamento de erros

defer funciona eficazmente com funções throws. Quando uma função pode lançar um erro em qualquer fase, defer garante a limpeza sem duplicar código em cada bloco catch ou saída antecipada com 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) e logAudit(user) serão chamados
    // em qualquer throw ou return
}

Importante: defer executa antes de o controlo ser transferido para fora do bloco catch, mas depois de o erro ocorrer. Se um erro é lançado dentro de defer, o Swift não permite usar try diretamente dentro de defer — precisa de try? ou try!. Segundo a Documentação da Apple, o Swift não permite que um erro escape do defer, pois isso violaria a garantia de execução do bloco.

Coloque defer imediatamente após a aquisição do recurso. Isto segue o princípio da proximidade: o leitor vê a aquisição e a libertação juntas, o que melhora a fiabilidade do código e simplifica a revisão de código.

Regras de âmbito

defer executa ao sair do âmbito em que é declarado. Se defer é declarado dentro de um bloco do, executa ao sair desse bloco, não da função externa. Se está dentro de um ciclo for — em cada iteração:

swift
func scopeExample() {
    print("start")
    do {
        defer { print("defer do bloco do") }
        print("inside do")
    }
    // "defer do bloco do" imprime aqui
    print("after do")
}
    // Saída: start, inside do, defer do bloco do, after do

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

As variáveis capturadas por defer são lidas no momento de saída do âmbito, não no momento da declaração do defer. Se uma variável muda entre a declaração do defer e o fim do âmbito, defer verá o último valor. Esta é uma distinção importante dos closures, onde a captura ocorre no momento da criação. Tenha cuidado: alterações a uma variável após declarar defer afetarão a sua execução.

Erros comuns com defer

O primeiro erro — assumir uma ordem de execução diferente de LIFO. Se a ordem de limpeza for importante e os defers forem declarados na ordem errada, os recursos podem ser libertados com violações de dependência. Solução: declare defer imediatamente após capturar cada recurso. Segundo recurso aberto → defer { close second } antes de o primeiro ser fechado.

O segundo erro — usar defer para lógica não relacionada com limpeza. defer é para limpeza garantida, não para controlo de fluxo principal. Se o código dentro de defer afeta o valor de retorno, é quase sempre um erro. defer não pode alterar o valor de retorno de uma função (ao contrário do Java finally, onde return em finally sobrescreve o return original).

O terceiro erro — lançar um erro a partir de defer. O Swift proíbe try dentro de defer se o erro puder propagar-se para fora. Use try? ou try! para operações que possam lançar um erro, ou envolva-as numa função separada sem throws. Segundo O’Reilly “Swift in Depth” (2025), uma boa prática é tornar as funções de limpeza non-throwing ou tratar os erros dentro de defer.

Perguntas Frequentes

O que é defer em Swift?

defer é uma construção Swift que adia a execução de um bloco até que o âmbito atual termine. O bloco executa sempre — em return, throw, break ou conclusão normal. É usado para limpeza garantida de recursos: fechar ficheiros, libertar bloqueios.

Por que ordem executam vários defers?

Por ordem inversa à declaração (LIFO) — o último defer declarado executa primeiro. Isto garante uma limpeza correta de recursos aninhados: se o recurso B é aberto depois de A, será fechado antes de A, prevenindo dependências de recursos já libertados.

Pode-se lançar um erro a partir de defer?

Não diretamente — o Swift impede a propagação de erros a partir de defer. Use try? ou try! para operações que possam lançar um erro. A melhor prática é tornar as funções de limpeza non-throwing ou tratar os erros dentro de defer sem os propagar para fora.

Qual é a diferença entre defer e do-catch-finally?

defer está ligado a um âmbito e executa em qualquer saída, incluindo return, throw e break. finally (noutras linguagens) está ligado a try-catch e só executa quando há try. O Swift não tem finally — defer cobre completamente este cenário e funciona para qualquer âmbito, não apenas para tratamento de erros.

Defer vê alterações de variáveis após a sua declaração?

Sim, defer lê as variáveis no momento de saída do âmbito, não no momento da declaração. Se uma variável muda depois de defer ser declarado, o bloco defer verá o último valor. Isto difere dos closures normais, onde a captura é fixada no momento da criação.

Resumo

  • defer — um bloco de finalização executado ao sair do âmbito independentemente do motivo (return, throw, break, conclusão normal)
  • Ordem LIFO — vários defers executam de baixo para cima, o último declarado executa primeiro
  • Limpeza de recursos — caso de uso principal: fechar ficheiros, libertar bloqueios, libertar ponteiros, parar animações
  • Compatibilidade com throws: defer executa após um erro mas antes de sair do bloco catch; erros de defer não se propagam
  • Âmbito: defer executa ao sair do âmbito onde foi declarado — bloco do, ciclo, função
  • Captura de variáveis: defer lê os valores no momento de saída, não no momento de declaração — cuidado com mutações
  • Melhor prática: declare defer imediatamente após adquirir o recurso, não use defer para lógica de negócio, torne as funções de limpeza non-throwing

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também