← Назад к вопросам

Что такое closure?

2.0 Middle🔥 142 комментариев
#CI/CD и инструменты разработки#Soft Skills и карьера

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое замыкание (Closure) в Swift?

Замыкание (Closure) — это автономный блок функциональности, который может быть передан и использован в вашем коде. Проще говоря, это функция без имени, которая может захватывать и хранить ссылки на переменные и константы из окружающего контекста, в котором она объявлена. В Swift функции являются частным случаем замыканий.

Ключевые характеристики замыканий

  1. Захват контекста (Capturing Values): Самая важная особенность. Замыкание может захватывать и хранить ссылки на любые константы и переменные из окружающей области видимости, в которой оно объявлено. Даже если исходная область видимости уже не существует, замыкание может продолжать использовать и изменять эти значения.
  2. Ссылочный тип (Reference Type): Как и классы, замыкания являются ссылочными типами. Когда вы присваиваете замыкание константе или переменной, вы фактически присваиваете ссылку на это замыкание.
  3. Синтаксическое упрощение: Swift предлагает несколько синтаксических оптимизаций для более лаконичной записи замыканий: вывод типа контекста, неявные возвращаемые значения для однострочных замыканий, сокращенные имена аргументов и trailing-синтаксис.

Синтаксис и эволюция от функции к замыканию

Рассмотрим на примере. Допустим, у нас есть функция для сортировки массива.

1. Полная форма функции:

func descending(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
}
let names = ["Анна", "Борис", "Алексей"]
let sortedNames = names.sorted(by: descending)
// Результат: ["Борис", "Анна", "Алексей"]

2. Замыкающее выражение (полная форма): Та же логика, но как безымянное замыкание, присвоенное константе.

let descendingClosure: (String, String) -> Bool = { (s1: String, s2: String) -> Bool in
    return s1 > s2
}
let sortedNames = names.sorted(by: descendingClosure)

3. Сокращение замыкания благодаря выводу типа: Компилятор Swift знает, что метод sorted(by:) ожидает замыкание типа (String, String) -> Bool. Можно опустить типы параметров и возвращаемого значения.

let sortedNames = names.sorted(by: { s1, s2 in return s1 > s2 })

4. Неявный возврат (Implicit Returns): Для однострочных замыканий можно опустить ключевое слово return.

let sortedNames = names.sorted(by: { s1, s2 in s1 > s2 })

5. Сокращенные имена аргументов (Shorthand Argument Names): Swift предоставляет автоматические имена аргументов в виде $0, $1, $2 и т.д.

let sortedNames = names.sorted(by: { $0 > $1 })

6. Операторные функции (Operator Methods): Так как для строк существует оператор >, который как раз соответствует нужной сигнатуре (String, String) -> Bool, можно передать его напрямую.

let sortedNames = names.sorted(by: >)

7. Последующее замыкание (Trailing Closure): Если замыкание является последним аргументом функции, его можно вынести за круглые скобки.

let sortedNames = names.sorted() { $0 > $1 }
// А если замыкание - единственный аргумент, круглые скобки можно опустить.
let sortedNames = names.sorted { $0 > $1 }

Захват значений (Capturing Values)

Это мощнейшая особенность. При захвате замыкание удерживает (retain) захваченные ссылки, что может приводить к циклам сильных ссылок (strong reference cycles).

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0 // Локальная переменная функции
    func incrementer() -> Int {
        runningTotal += amount // Захват runningTotal и amount!
        return runningTotal
    }
    return incrementer // Функция возвращает ВЛОЖЕННУЮ функцию-замыкание
}

let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // 10
print(incrementByTen()) // 20
print(incrementByTen()) // 30
// Несмотря на то, что контекст функции makeIncrementer завершился,
// замыкание incrementByTen сохранило ("захватило") runningTotal и amount.

Автозамыкания (Autoclosures)

Автозамыкание — это замыкание, которое автоматически создается для обертки выражения, которое передается в качестве аргумента функции. Оно не принимает параметров и возвращает значение выражения, обернутого в него. Позволяет отложить выполнение кода.

var customersInLine = ["Евгений", "Ольга", "Дмитрий"]
func serve(customer provider: @autoclosure () -> String) {
    print("Обслуживаем \(provider())!")
}
serve(customer: customersInLine.remove(at: 0)) // Выражение customersInLine.remove(at: 0) автоматически оборачивается в замыкание.
// Код удаления элемента из массива выполнится только в момент вызова provider() внутри функции serve.

Циклы сильных ссылок и capture lists

Из-за захвата ссылок замыкания могут создавать циклы сильных ссылок. Для их разрыва используют списки захвата (capture lists).

class SomeClass {
    var value = 10
    lazy var someClosure: (() -> Void) = { [weak self] in // Захватываем self как weak ссылку
        guard let self = self else { return }
        print("Значение: \(self.value)")
    }
}

В списке захвата [weak self] указывается, что self должен быть захвачен как слабая (weak) или неподвластная (unowned) ссылка, предотвращая утечку памяти.

Итог: Замыкания в Swift — это краеугольный камень функционального программирования в языке. Они широко используются для асинхронных операций (завершающие обработчики/completion handlers), обратных вызовов, модификации поведения функций (как в примере с sorted(by:)), а также являются основой для многих конструкций стандартной библиотеки, таких как map, filter, reduce. Понимание механизма захвата значений и управления памятью является критически важным для написания корректного и эффективного кода на Swift.