Как замыкание (closure) работает с экземпляром структуры в Swift?

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

Ответ

Замыкание захватывает экземпляр структуры по значению, так как структуры являются типами-значениями. Это означает создание копии структуры в момент захвата.

Ключевые моменты:

  • Для escaping-замыканий копия создается в момент захвата.
  • Для изменения (мутирования) структуры внутри замыкания необходимо:
    1. Объявить экземпляр структуры как var.
    2. Использовать mutating методы.

Пример:

struct Counter {
    var value = 0

    mutating func increment() {
        value += 1
    }
}

var counter = Counter()

// Захват неизменяемой копии
let readClosure = { [counter] in
    print(counter.value) // Используется копия, захваченная при создании замыкания
}

// Попытка мутирования требует `var` counter в текущей области видимости
let mutateClosure = {
    counter.increment() // Работает, так как counter объявлен как `var`
}

// Для escaping-замыкания мутирование невозможно, так как работает с копией
var escapingClosure: (() -> Void)?
escapingClosure = { [counter] in
    // counter здесь — неизменяемая копия. counter.increment() — ошибка компиляции.
}

Почему так? Это поведение гарантирует безопасность работы с типами-значениями в асинхронном контексте, предотвращая неожиданные изменения данных после захвата.