Можно ли хранить замыкания в массиве в Swift?

«Можно ли хранить замыкания в массиве в Swift?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, можно. В Swift замыкания (closures) являются first-class типами, что позволяет присваивать их переменным, передавать как аргументы и хранить в коллекциях, включая массивы.

Базовый пример:

// Массив замыканий, не принимающих и не возвращающих значений
var handlers: [() -> Void] = []

let handler1 = { print("Task 1 completed") }
let handler2 = { print("Task 2 completed") }

handlers.append(handler1)
handlers.append(handler2)

// Выполнить все замыкания
handlers.forEach { $0() }
// Вывод:
// Task 1 completed
// Task 2 completed

Практическое применение:

  • Цепочки обратных вызовов (callback chains)
  • Массивы обработчиков событий (например, кнопка с несколькими действиями)
  • Отложенное выполнение задач

Критически важные нюансы:

  1. Явное указание типа: Компилятору часто требуется явная аннотация типа массива.
  2. Циклы сильных ссылок: Если замыкание захватывает self, это создаёт сильную ссылку.

    // ❌ Риск утечки памяти
    closures.append { self.doSomething() }
    
    // ✅ Безопасный захват
    closures.append { [weak self] in
        self?.doSomething()
    }
  3. Изменяемость: Если массив объявлен как let, вы не сможете добавлять или удалять замыкания.