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 é 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.
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:
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.
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:
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 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:
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.
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:
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.
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
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 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.
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.
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.
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
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.
Leia também