Ответ
Да, можно. Поскольку замыкания в 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() {} }
Ответ 18+ 🔞
О, смотри-ка, какой интересный вопрос подвалил! Можно ли засунуть замыкания в массив? Да хули нет-то, конечно можно, они же в 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
Видишь? Всё просто, как три копейки. Собрал их в кучу и потом вызывай, когда вздумается. Но тут, блядь, есть свои подводные грабли, о которые можно ебалом приложиться.
Первое — типизация. Все эти замыкания в массиве должны быть как близнецы-братья, с одинаковой сигнатурой. Нельзя в один массив пихнуть () -> Void и (Int) -> String. Компилятор тебе такого не простит, сразу начнёт орать, как сука.
Второе — захват переменных. Замыкания по умолчанию хватают всё, что вокруг них болтается, за горло сильной ссылкой. Это как тот пьяный друг на вечеринке, который вцепился в тебя и не отпускает.
И третье, самое пиздатое — циклы сильных ссылок. Вот представь: у тебя есть класс, который хранит массив замыканий. А внутри замыкания ты обращаешься к self. И получается, что self держит массив, а замыкание в массиве держит self. И они обнимаются, и никто не может умереть. Классический мексиканский сериал, ёпта! Чтобы этого не было, надо использовать списки захвата — [weak self] или [unowned self].
class Controller {
var handlers: [() -> Void] = []
func setupHandler() {
// БЕЗОПАСНО: предотвращаем цикл сильных ссылок
handlers.append { [weak self] in
self?.doWork()
}
}
func doWork() {}
}
Вот так, сука, и живём. Слабость — наше всё. Используй [weak self], и спи спокойно, не боясь утечек памяти. А то потом будешь как Герасим — Муму топить, только уже не собачку, а свою программу.