Ответ
Да, замыкания в Swift являются типами первого класса, поэтому их можно хранить в массивах, словарях и других коллекциях.
Базовый пример:
// Массив замыканий без параметров, возвращающих Void
var tasks: [() -> Void] = []
tasks.append { print("Task 1") }
tasks.append { print("Task 2") }
// Выполнение всех замыканий в массиве
for task in tasks {
task() // Выведет "Task 1", затем "Task 2"
}
Пример с параметрами и возвращаемым значением:
// Массив замыканий, преобразующих Int в String
let transformers: [(Int) -> String] = [
{ "Number: ($0)" },
{ String(repeating: "*($0)", count: 3) },
{ _ in "Constant" }
]
let results = transformers.map { $0(5) }
// results = ["Number: 5", "*5*5*5", "Constant"]
Критически важные аспекты:
- Единая сигнатура: Все замыкания в массиве должны иметь идентичную сигнатуру (типы параметров и возвращаемого значения).
-
Циклы сильных ссылок: Если замыкание захватывает
selfили другие объекты, это может создать retain cycle.class Controller { var closures: [() -> Void] = [] var value = 10 func addClosure() { // ВНИМАНИЕ: Создается сильная ссылка на `self` closures.append { print(self.value) } // Решение: использовать capture list [weak self] closures.append { [weak self] in print(self?.value ?? 0) } } } - Типизация: Для сложных сигнатур используйте
typealiasдля читаемости.typealias CompletionHandler = (Result<Data, Error>) -> Void var completions: [CompletionHandler] = []
Ответ 18+ 🔞
Слушай, а ведь замыкания в Swift — это как те самые универсальные солдатики, их можно в кучки собирать, в массивы пихать, в словари запихивать, да куда угодно, блядь! Типы первого класса, ёпта, вот это мощь!
Смотри, как просто, на примере:
// Массив замыканий без параметров, возвращающих Void
var tasks: [() -> Void] = []
tasks.append { print("Task 1") }
tasks.append { print("Task 2") }
// Выполнение всех замыканий в массиве
for task in tasks {
task() // Выведет "Task 1", затем "Task 2"
}
Вот, собрал задачники в кучку и по очереди их всех запустил — красота, блядь!
А вот если с параметрами поиграться:
// Массив замыканий, преобразующих Int в String
let transformers: [(Int) -> String] = [
{ "Number: ($0)" },
{ String(repeating: "*($0)", count: 3) },
{ _ in "Constant" }
]
let results = transformers.map { $0(5) }
// results = ["Number: 5", "*5*5*5", "Constant"]
Три разных замыкания, но сигнатура одна — (Int) -> String. Как три клоуна в одном цирке, но делают разное, сука!
А теперь, внимание, ебаный рот, важные моменты, где можно на граблях встать:
-
Единая сигнатура, мать её! Все замыкания в массиве должны быть как близнецы-братья — одинаковые параметры и возвращаемый тип. Нельзя в одну кучу пихать
(Int) -> Stringи() -> Void. Компилятор тебе мозги выест, блядь. -
Циклы сильных ссылок — вот это пиздец! Самый частый подводный камень. Если замыкание хватает
selfи ты его в массив суёшь, а массив живет в этом жеself— получается хоровод, где все держатся за руки и никто не умирает. Память течёт, как суп через дырявое сито.class Controller { var closures: [() -> Void] = [] var value = 10 func addClosure() { // ВНИМАНИЕ: Создается сильная ссылка на `self` — ПИЗДЕЦ, УТЕЧКА! closures.append { print(self.value) } // Решение, блядь: использовать capture list [weak self] closures.append { [weak self] in print(self?.value ?? 0) } } }Видишь? В первом случае
selfи массивclosuresбудут вечно любить друг друга. Во втором — слабая ссылка,[weak self], это как сказать: «Дружим, но если ты умрёшь — я не повешусь». -
Типизация для читаемости. Когда сигнатура замыкания длиннее, чем очередь за колбасой в девяностые, используй
typealias. Глаза не сломаешь.typealias CompletionHandler = (Result<Data, Error>) -> Void var completions: [CompletionHandler] = []Согласись,
[CompletionHandler]выглядит куда приятнее, чем[(Result<Data, Error>) -> Void]. Ёперный театр!
Короче, инструмент мощный, но, как и болгарка, требует уважения. А то отхватишь не того, блядь.