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

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

Ответ

Да, можно. Поскольку замыкания в Swift являются ссылочными типами, их можно хранить в коллекциях, таких как массивы. Для этого необходимо явно указать тип элементов массива как тип замыкания.

Пример:

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

func addClosure(_ closure: @escaping () -> Void) {
    completionHandlers.append(closure)
}

addClosure { print("Выполнено задание 1") }
addClosure { print("Выполнено задание 2") }

// Последовательный вызов всех замыканий
for handler in completionHandlers {
    handler()
}
// Вывод:
// Выполнено задание 1
// Выполнено задание 2

Важные аспекты:

  • Типизация. Все замыкания в массиве должны иметь идентичную сигнатуру (тип принимаемых и возвращаемых значений).
  • Захват переменных (capture). Замыкания захватывают переменные из окружающего контекста по сильной ссылке (strong reference) по умолчанию.
  • Циклы сильных ссылок. Если замыкание захватывает self, а self хранит этот массив, может возникнуть цикл удержания. Используйте списки захвата [weak self] или [unowned self].
    class Controller {
    var handlers: [() -> Void] = []
    func setupHandler() {
        // БЕЗОПАСНО: предотвращаем цикл сильных ссылок
        handlers.append { [weak self] in
            self?.doWork()
        }
    }
    func doWork() {}
    }