Являются ли замыкания (closures) в Swift ссылочным (reference) или значимым (value) типом?

Ответ

Замыкания в Swift являются ссылочными типами (reference types). Они размещаются в куче (heap), и при присваивании замыкания новой переменной или передаче в функцию копируется ссылка на один и тот же экземпляр замыкания, а не его содержимое.

Почему это важно:

  • Изменения состояния переменных, захваченных замыканием (captured variables), видны всем владельцам ссылки на это замыкание.
  • Это поведение отличается от структур (value types), которые копируются.

Пример, демонстрирующий reference semantics:

var counter = 0
let incrementer = { counter += 1 } // Замыкание захватывает `counter` по сильной ссылке

let anotherReference = incrementer // Копируется ссылка, а не новое замыкание

incrementer() // counter = 1
anotherReference() // counter = 2
print(counter) // Выведет: 2

Управление захватом: Чтобы избежать непреднамеренного совместного использования состояния, используйте списки захвата (capture lists). Например, [counter] захватит копию значения на момент создания замыкания.

Ответ 18+ 🔞

Слушай, а вот замыкания в Swift — это же, блядь, чистой воды ссылочные типы, как классы! Не как структуры, которые тупо копируются, а вот эта вот, понимаешь, ебаная сущность, которая живёт в куче. Как ты её ни присваивай, ни передавай — ты везде таскаешь одну и ту же пиздатую ссылку на один и тот же кусок памяти. Один экземпляр на всех, как последняя пачка сигарет в роте.

И в чём же, блядь, подвох? А подвох в том, что если твоё замыкание что-то там внутри себя меняет — какую-нибудь захваченную переменную — то все, у кого есть ссылка на это замыкание, увидят эти изменения. Это не как с Int или String, где у каждого своя копия и похуй. Тут всё сообща, как в коммуналке.

Смотри, какой пиздатый пример:

var counter = 0
let incrementer = { counter += 1 } // Замыкание схватило `counter` и не отпускает, жадная сука

let anotherReference = incrementer // Скопировалась не суть, а ссылка! Теперь они как сиамские близнецы.

incrementer() // counter = 1
anotherReference() // counter = 2
print(counter) // Выведет: 2. Охуеть, да? Обе ссылки долбили один и тот же счётчик!

Как не облажаться? А вот для этого, друг мой, есть списки захвата. Хочешь заморозить значение на момент создания, а не тащить живую ссылку? Пожалуйста! Захвати [counter] — и будет тебе отдельная, ни от кого не зависящая копия. Как будто сфоткал и носил с собой, а оригинал пусть себе живёт своей жизнью.