← Назад к вопросам

Можно ли положить closure в массив?

1.3 Junior🔥 221 комментариев
#Управление памятью#Язык Swift

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Да, можно! И это мощный инструмент

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

Как это работает

Массивы в Swift типизированы — они хранят элементы одного типа. Но Closure — это тоже тип! Поэтому если объявить массив, где тип элемента — конкретная сигнатура closure, то можно добавлять туда замыкания, соответствующие этой сигнатуре.

Примеры

Простейший случай: массив замыканий без параметров

// Определяем тип: closure без параметров, возвращающий String
typealias SimpleClosure = () -> String

// Создаём массив таких closure'ов
var closureArray: [SimpleClosure] = []

// Добавляем несколько замыканий в массив
closureArray.append({
    return "Привет от первого замыкания!"
})

closureArray.append {
    "А вот и второе!"
}

// Можно и так, с явным указанием типа
let thirdClosure: SimpleClosure = { "Третье в списке!" }
closureArray.append(thirdClosure)

// Выполняем все closure'ы из массива
for closure in closureArray {
    let result = closure()
    print(result)
}
// Вывод:
// Привет от первого замыкания!
// А вот и второе!
// Третье в списке!

Более практичный пример: массив обработчиков

// Тип для обработчика событий с параметром
typealias EventHandler = (String) -> Void

class EventManager {
    private var handlers: [EventHandler] = []
    
    // Добавляем обработчик в массив
    func addHandler(_ handler: @escaping EventHandler) {
        handlers.append(handler)
    }
    
    // Вызываем все обработчики
    func triggerEvent(message: String) {
        handlers.forEach { handler in
            handler(message)
        }
    }
}

// Использование
let manager = EventManager()

// Добавляем разные обработчики
manager.addHandler { message in
    print("Логгер: \(message)")
}

manager.addHandler { message in
    print("Виджет получил: \(message)")
}

manager.addHandler { message in
    if message.contains("ошибка") {
        print("⚠️ Обнаружена ошибка!")
    }
}

// Вызываем все обработчики
manager.triggerEvent(message: "Пользователь вошёл в систему")
manager.triggerEvent(message: "Произошла ошибка сети")

Важные особенности и пример с @escaping

// @escaping часто необходим, так как замыкания сохраняются в массиве
// для последующего использования

var completionHandlers: [() -> Void] = []

func addCompletionHandler(_ handler: @escaping () -> Void) {
    completionHandlers.append(handler)
    // Без @escaping компилятор выдаст ошибку, так как closure
    // "сбегает" из текущей области видимости
}

// Пример использования с захватом self
class DataLoader {
    private var data = [String]()
    
    func loadData(completion: @escaping ([String]) -> Void) {
        // Симуляция асинхронной загрузки
        DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
            self.data = ["Данные 1", "Данные 2", "Данные 3"]
            DispatchQueue.main.async {
                completion(self.data)
            }
        }
    }
}

let loader = DataLoader()
var callbacks: [([String]) -> Void] = []

callbacks.append { data in
    print("Первый обработчик получил: \(data.count) элементов")
}

callbacks.append { data in
    print("Второй обработчик: \(data.joined(separator: ", "))")
}

loader.loadData { data in
    // Вызываем все callback'и из массива при получении данных
    callbacks.forEach { callback in
        callback(data)
    }
}

Преимущества и сценарии использования

Хранение closure в массивах особенно полезно в следующих случаях:

  • Системы событий и уведомлений — когда нужно уведомить несколько подписчиков
  • Цепочки обработчиков (middleware) — последовательная обработка данных
  • Обработка асинхронных операций — коллекция completion handlers
  • Паттерны стратегии — выбор алгоритма из набора доступных
  • Конфигурирование UI — набор анимаций или обработчиков жестов

Ключевое ограничение

Все closure в массиве должны иметь одинаковую сигнатуру (типы параметров и возвращаемого значения). Если нужна разнородная коллекция, можно использовать массив типа [Any], но это лишит вас типобезопасности и потребует приведения типов.

Таким образом, closure в массивах — это не только возможно, но и активно используется в профессиональной iOS-разработке для создания гибких, декомпонованных архитектур.

Можно ли положить closure в массив? | PrepBro