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

Но что такое замыкания? Как они работают? И когда вы должны их использовать?

В этой статье я объясню замыкания и варианты их практического использования для начинающих, средних и продвинутых программистов. Я также покажу вам несколько примеров того, как писать и использовать замыкания в Swift.

Что такое замыкания?

Согласно официальной документации**1** замыканиями являются:

автономные блоки функций, которые можно передавать и использовать в коде. Замыкания могут захватывать и хранить ссылки на любые константы и переменные из контекста, в котором они определены. Это называется закрытием этих констант и переменных.

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

  • Вы не используете ключевое слово func для создания замыкания.
  • Вам не нужно давать замыканию имя (хотя вы можете присвоить его переменной или константе).
  • Вы можете опустить типы параметров и возвращаемый тип, если их можно вывести из контекста.
  • Вы можете использовать сокращенные имена аргументов ($0, $1 и т. д.) вместо того, чтобы указывать их явно.
  • Вы можете написать замыкание как завершающее замыкание, если оно является последним аргументом функции или метода.

Вот пример того, как объявить простое замыкание, которое печатает «Hello World»:

// Declare a closure
let hello = {
    print("Hello World")
}

// Call the closure
hello()

Обратите внимание, как мы использовали {}, чтобы заключить тело замыкания, и как мы вызывали его, используя (), как обычную функцию.

Зачем использовать замыкания?

Замыкания имеют много преимуществ по сравнению с обычными функциями. Некоторые из них:

  • Они делают ваш код более кратким и читабельным. Вам не нужно писать дополнительные строки кода, чтобы определить именованную функцию, которую вы используете только один или два раза. Вы можете просто написать встроенное замыкание там, где вам это нужно.
  • Они делают ваш код более гибким. Вы можете передавать замыкания в качестве аргументов другим функциям или методам или возвращать их как значения из других функций или методов. Это позволяет вам создавать функции более высокого порядка, которые работают с другими функциями (такими как отображение, фильтрация, уменьшение и т. д.).
  • Они делают ваш код более выразительным. Вы можете использовать замыкания для создания пользовательских операторов или DSL (предметно-ориентированных языков), которые соответствуют вашим потребностям. Например, вы можете использовать замыкания для создания анимаций, сетевых запросов, правил проверки и т. д.
  • Они делают ваш код более эффективным. Вы можете использовать замыкания для захвата значений из окружающей их области без их копирования или создания сильных ссылок. Это позволяет избежать утечек памяти или сохранения циклов.

Как использовать замыкания?

Есть много способов использовать замыкания в Swift. Вот несколько распространенных сценариев, в которых замыкания могут пригодиться:

Как обратные вызовы

Одно из наиболее распространенных применений замыканий — это обратные вызовы: блоки кода, которые выполняются после завершения какой-либо асинхронной операции (например, сетевых запросов, таймеров, анимации и т. д.).

Например, предположим, что вы хотите получить некоторые данные из API с помощью URLSession:

// Create a URL
let url = URL(string: "<https://example.com/api/data>")!

// Create a URLSessionDataTask
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    // This closure is executed when the task finishes
    // Check for errors
    if let error = error {
        print("Error: \\(error.localizedDescription)")
        return
    }

    // Check for data
    guard let data = data else {
        print("No data received")
        return
    }

    // Do something with data (e.g., parse JSON)
}

Обратите внимание, как мы передали замыкание в качестве аргумента dataTask(with:completionHandler:)method2. Это замыкание принимает три параметра: data, response и error. Закрытие выполняется, когда задача завершает выборку данных из URL-адреса.

Мы могли бы также записать это замыкание как замыкающее замыкание:

// Create a URL
let url = URL(string: "<https://example.com/api/data>")!

// Create a URLSessionDataTask using trailing closure syntax
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    // This closure is executed when the task finishes
    // Check for errors
    if let error = error {
        print("Error: \\(error.localizedDescription)")
        return
    }

    // Check for data
    guard let data = data else {
        print("No data received")
        return
    }

    // Do something with data (e.g., parse JSON)
}

// Alternatively, you can write the closure after the parentheses
let task = URLSession.shared.dataTask(with: url) {
    data, response, error in
    // Closure body goes here
}

Это называется синтаксис завершающего замыкания1, и он делает ваш код более кратким и читабельным. Вы можете использовать этот синтаксис всякий раз, когда замыкание является последним аргументом функции или метода.

Как функции высшего порядка

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

Например, предположим, что у вас есть массив чисел:

let numbers = [1, 2, 3, 4, 5]

Вы можете использовать функцию map для преобразования каждого элемента массива с помощью замыкания:

// Use map to double each element of the array
let doubled = numbers.map { number in
    number * 2
}

// Alternatively, you can use shorthand argument names ($0)
let doubled = numbers.map { $0 * 2 }

// Print doubled array [2, 4, 6 ,8 ,10]
print(doubled)

Функция map принимает замыкание в качестве аргумента и применяет его к каждому элементу массива. Результатом является новый массив с преобразованными значениями.

Вы также можете использовать другие функции более высокого порядка, такие как filter, reduce, forEach и т. д., для выполнения различных операций с коллекциями с использованием замыканий.

Как пользовательские операторы

Вы также можете использовать замыкания для создания пользовательских операторов, выполняющих определенные задачи. Например, предположим, что вы хотите создать оператор, который добавляет две строки с пробелом между ними:

// Define an infix operator named +-
infix operator +-

// Define a function that implements this operator using a closure
func +- (left: String, right: String) -> String {
    return left + " " + right
}

// Use this operator to concatenate strings with a space between them

let helloWorld = "Hello" +- "World" // "Hello World"
let swiftRocks = "Swift" +- "Rocks" // "Swift Rocks"

Обратите внимание, как мы использовали замыкание для определения функции, реализующей наш пользовательский оператор. Мы также могли бы написать эту функцию, используя обычный синтаксис:

func +- (left: String, right: String) -> String {
    func addSpace(_ left: String,_ right: String) -> String {
        return left + " " + right
    }

    return addSpace(left,right)
}

Но использование замыкания делает его более лаконичным и элегантным.

Заключение

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

В этой статье я объяснил основы замыканий и варианты их практического использования для начинающих, средних и продвинутых программистов. Надеюсь, вы узнали что-то новое и полезное из этой статьи.

Если вы хотите узнать больше о замыканиях и других функциях Swift, я рекомендую ознакомиться со следующими ресурсами:

Спасибо за чтение и счастливого кодирования! 😊