Swift: Closures do Swift

Closures são blocos autônomos de funcionalidade no Swift que podem capturar e armazenar referências a variáveis e constantes do seu contexto circundante. Esta lição começa com expressões de closure e avança até dominar um dos recursos de linguagem mais flexíveis do Swift.

1. O Que Você Vai Aprender


2. Uma História Real de uma Analista de Dados

(1) Problema: Lógica de Ordenação Não Reutilizável e Código Verboso

Alice precisa ordenar dados de usuário — por nome, por idade, por data de cadastro. Sua abordagem inicial é escrever uma função separada para cada ordenação:

SWIFT
func sortByName(_ a: [String: Any], _ b: [String: Any]) -> Bool {
    return (a["name"] as! String) < (b["name"] as! String)
}
func sortByAge(_ a: [String: Any], _ b: [String: Any]) -> Bool {
    return (a["age"] as! Int) < (b["age"] as! Int)
}

Toda vez que a regra de ordenação muda, ela precisa escrever uma nova função, causando inchaço no código. A lógica de ordenação está espalhada por múltiplas funções — você não pode especificá-la dinamicamente no local da chamada.

(2) A Solução com Closures

As closures do Swift permitem embutir a regra de ordenação diretamente no local da chamada sorted(by:):

SWIFT
let sortedByAge = users.sorted { $0["age"] as! Int < $1["age"] as! Int }
let sortedByName = users.sorted { $0["name"] as! String < $1["name"] as! String }

A lógica de ordenação passa de "escrever uma função → passá-la" para "defini-la onde você a usa".

(3) Benefício: Definição Embutida, Troca Flexível

Dimensão Função Isolada Closure Embutida
Linhas de Código 3-5 linhas por ordenação 1 linha
Legibilidade Precisa encontrar a definição da função Lógica visível no local da chamada
Flexibilidade Todas as estratégias devem ser pré-definidas Definida na hora
Acesso ao Contexto Não pode capturar variáveis externas Pode capturar estado contextual

3. Expressões de Closure

(1) Sintaxe Básica

Uma expressão de closure é uma maneira concisa de escrever uma closure. A sintaxe completa é:

100%
graph LR
    A[Expressão de Closure] --> B["{ (parâmetros) -> TipoRetorno in"]
    A --> C["    instruções"]
    A --> D["}"]
    B --> E["{ (a: Int, b: Int) -> Bool in"]
    C --> F["    return a < b"]
    D --> G["}"]
Componente Obrigatório Observações
{ } Sim Closures são envolvidas por chaves
Lista de parâmetros Não Pode ser omitida usando nomes abreviados
-> TipoRetorno Não Pode ser inferido automaticamente
in Condicional Obrigatório quando parâmetros ou tipo de retorno são declarados

▶ Exemplo: Três Maneiras de Escrever Expressões de Closure

SWIFT
// ============================================
// Three ways to write closures for sorted(by:)
// ============================================
let numbers = [5, 2, 8, 1, 9]
// Approach 1: Full closure expression
let sorted1 = numbers.sorted(by: { (a: Int, b: Int) -> Bool in
    return a < b
})
// Approach 2: Type inference (omit parameter types and return type)
let sorted2 = numbers.sorted(by: { a, b in return a < b })
// Approach 3: Omit return (single-expression implicit return)
let sorted3 = numbers.sorted(by: { a, b in a < b })
print("sorted3: \(sorted3)")

Saída:

TEXT 📖 Somente leitura
sorted3: [1, 2, 5, 8, 9]

4. Closures à Direita e Nomes Abreviados de Parâmetro

(1) Closures à Direita

Quando uma closure é o último argumento de uma função, você pode movê-la para fora dos parênteses:

Estilo Exemplo
Padrão sorted(by: { a, b in a < b })
À direita sorted { a, b in a < b }
Multi-parâmetro à direita animate(duration: 1) { ... }

▶ Exemplo: Simplificando Chamadas com Closures à Direita

SWIFT
// ============================================
// Trailing closure concise syntax
// ============================================
let scores = [78, 92, 85, 68, 99]
// Standard syntax
let ascending1 = scores.sorted(by: { a, b in a < b })
// Trailing closure syntax
let ascending2 = scores.sorted { a, b in a < b }
print("Ascending: \(ascending2)")
// Multiple closure parameters also supported
func performRequest(url: String, onSuccess: (String) -> Void, onError: (Error) -> Void) {
    // Simulate network request
    onSuccess("Data received")
}
performRequest(url: "https://api.example.com") { data in
    print("Success: \(data)")
} onError: { error in
    print("Error: \(error)")
}

Saída:

TEXT 📖 Somente leitura
Ascending: [68, 78, 85, 92, 99]
Success: Data received

(2) Nomes Abreviados de Parâmetro

O Swift fornece automaticamente nomes abreviados para parâmetros de closure inline: $0, $1, $2, ...

Sintaxe Completa Abreviada
{ a, b in a < b } { $0 < $1 }
{ nome in print(nome) } { print($0) }
{ a, b, c in a + b + c } { $0 + $1 + $2 }

▶ Exemplo: Nomes Abreviados de Parâmetro em Ação

SWIFT
// ============================================
// Step-by-step simplification with shorthand names
// ============================================
let words = ["banana", "apple", "cherry", "date"]
// Full syntax
let sortedA = words.sorted(by: { (a: String, b: String) -> Bool in
    return a < b
})
// Shorthand parameter names — most concise
let sortedB = words.sorted(by: { $0 < $1 })
// Even simpler — pass the operator function directly
let sortedC = words.sorted(by: <)
print(sortedC)

Saída:

TEXT 📖 Somente leitura
["apple", "banana", "cherry", "date"]

Armadilha Comum: Ao usar nomes abreviados de parâmetro, você pode omitir return se o corpo da closure contiver apenas uma única expressão. Se houver múltiplas instruções, você deve escrever return.


5. Captura de Valor

Uma closure pode capturar constantes e variáveis do seu contexto circundante, mesmo depois que esse contexto não existe mais.

100%
graph TB
    A[Função externa criarIncrementador] --> B["Declara variável total = 0"]
    A --> C["Retorna closure { total += quantidade; return total }"]
    C --> D[Closure captura referência para total]
    D --> E[Mesmo após criarIncrementador retornar]
    E --> F["total permanece vivo dentro da closure"]

▶ Exemplo: Closure Capturando Variáveis de Contexto

SWIFT
// ============================================
// Value capture — generating an accumulator
// ============================================
func makeIncrementer(step: Int) -> () -> Int {
    var total = 0
    let incrementer: () -> Int = {
        total += step  // Captures total and step
        return total
    }
    return incrementer
}
let incrementByTwo = makeIncrementer(step: 2)
print(incrementByTwo()) // 2
print(incrementByTwo()) // 4
print(incrementByTwo()) // 6

Saída:

TEXT 📖 Somente leitura
2
4
6

Dica: A captura de valor é um dos recursos mais poderosos das closures. O Swift gerencia automaticamente a memória das variáveis capturadas — uma closure mantém uma referência forte às variáveis capturadas até que a própria closure seja liberada.

▶ Exemplo: Regras de Ordenação Personalizadas

SWIFT
// ============================================
// Custom sorting rules with closures
// ============================================
struct Product {
    let name: String
    let price: Double
    let rating: Int
}
let products = [
    Product(name: "Laptop", price: 1299, rating: 5),
    Product(name: "Mouse", price: 29, rating: 4),
    Product(name: "Keyboard", price: 99, rating: 5),
    Product(name: "Monitor", price: 399, rating: 3)
]
// Sort by price ascending
let byPrice = products.sorted { $0.price < $1.price }
print("Cheapest: \(byPrice.first!.name)")
// Sort by rating descending, then by price ascending on tie
let byRatingThenPrice = products.sorted {
    if $0.rating != $1.rating {
        return $0.rating > $1.rating
    }
    return $0.price < $1.price
}
print("Best value: \(byRatingThenPrice.first!.name)")

Saída:

TEXT 📖 Somente leitura
Cheapest: Mouse
Best value: Keyboard

6. Exemplo Completo: Ordenador de Lista de Usuários

SWIFT
// ============================================
// Complete example: User list sorter
// Features: Closure expressions + trailing closures + shorthand names + value capture
// ============================================
import Foundation
// 1. User model
struct User {
    let name: String
    let age: Int
    let score: Double
}
let users = [
    User(name: "Alice", age: 28, score: 92.5),
    User(name: "Bob", age: 22, score: 85.0),
    User(name: "Charlie", age: 35, score: 95.5),
    User(name: "Diana", age: 19, score: 78.0)
]
// 2. Sorter factory — captures sort direction
func makeSorter(ascending: Bool) -> (User, User) -> Bool {
    return { ascending ? $0.score < $1.score : $0.score > $1.score }
}
// 3. Sort using shorthand parameter names
let ascendingSorter = makeSorter(ascending: true)
let sortedAsc = users.sorted(by: ascendingSorter)
print("Ascending:")
for u in sortedAsc {
    print("  \(u.name): \(u.score)")
}
// 4. Inline sort with trailing closure
let sortedDesc = users.sorted { $0.score > $1.score }
print("Descending:")
for u in sortedDesc {
    print("  \(u.name): \(u.score)")
}

Saída:

TEXT 📖 Somente leitura
Ascending:
  Diana: 78.0
  Bob: 85.0
  Alice: 92.5
  Charlie: 95.5
Descending:
  Charlie: 95.5
  Alice: 92.5
  Bob: 85.0
  Diana: 78.0

❓ Perguntas Frequentes

P: Qual é a diferença entre uma closure e uma função? R: Closures são funções anônimas com uma sintaxe mais concisa. Funções têm nomes e podem ser chamadas repetidamente; closures são tipicamente usadas para cenários únicos. Closures podem capturar variáveis contextuais — funções aninhadas também podem, mas funções globais comuns não. P: Quando devo obrigatoriamente escrever a palavra-chave in? R: Quando uma expressão de closure declara uma lista de parâmetros ou tipo de retorno, você deve usar in para separar a declaração do corpo. Se você usar nomes abreviados de parâmetro (como $0) e omitir todas as anotações de tipo, in não é necessário. P: Como a ordem de $0 e $1 é determinada? R: $0 é o primeiro parâmetro da closure, $1 é o segundo, e assim por diante. A ordem é determinada pela ordem em que os argumentos são passados quando a closure é chamada, não pelos nomes dos parâmetros. P: Os valores capturados são cópias ou referências? R: Closures capturam variáveis por referência (semelhante a inout) — quando múltiplas closures compartilham a mesma variável, alterações em uma afetam a outra. Constantes são capturadas por cópia de valor. P: E se uma closure for muito longa e prejudicar a legibilidade? R: Se o corpo da closure exceder 5-10 linhas, extraia-o para uma função isolada ou uma variável de closure nomeada antes de passá-lo. Por exemplo: let ordenador = { a, b in ... }; sorted(by: ordenador).


📖 Resumo


📝 Exercícios

  1. Básico: Dado o array de inteiros [3, 7, 1, 9, 4, 6], escreva sorted(by:) de três maneiras diferentes (closure completa, inferência de tipo, nomes abreviados de parâmetro) para ordenar em ordem descendente.
  2. Intermediário: Escreva uma função repetirTarefa(vezes:tarefa:) que recebe uma contagem e uma closure, executando a closure o número especificado de vezes. Em seguida, chame-a para imprimir "Olá, Swift!" 5 vezes.
  3. Desafio: Implemente uma função criarCalculadora que recebe uma string de operador (+, -, *, /) e retorna a closure correspondente (Int, Int) -> Int. Em seguida, use a função para criar uma closure de adição e uma closure de multiplicação, calculando cada uma para (10, 5).
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%