Что происходит с копированием при захвате структуры функцией без capture list
Что проверяют
Различаете ли вы две разные вещи: семантику значения у структуры и правила захвата переменных замыканием. Формулировка подталкивает ответить «структуры копируются, значит копия», и интервьюер ждёт, поймаете ли вы подвох.
Как отвечать
Начните с разделения. Структура это значимый тип: присваивание и передача аргументом дают копию значения. Но замыкание без capture list захватывает не значение, а саму переменную, то есть держит то же хранилище, что и внешний код.
struct Point { var x = 0 }
var p = Point()
let byRef = { print(p.x) }
let byValue = { [p] in print(p.x) }
p.x = 42
byRef() // 42, переменная общая
byValue() // 0, копия снята при созданииДальше по пунктам:
- capture list копирует значение в момент создания замыкания, и дальше копия живёт отдельно;
- без capture list замыкание видит изменения переменной и само может её менять, если это
var; - убегающее замыкание продлевает жизнь захваченной переменной, она переезжает в кучу;
- копия структуры поверхностная:
ArrayиStringвнутри работают по правилу копирования при записи, буфер разделяется, пока в него не пишут; - для обычного параметра функции значение передаётся как заимствование, физического копирования при чтении может не быть вовсе.
Где спотыкаются
Считают, что значимый тип сам по себе гарантирует снимок. Снимок даёт только capture list, без него замыкание, созданное в одном состоянии и вызванное в другом, увидит новое значение. Это классический источник плавающих ошибок в обработчиках.
Забывают, что внутри структуры может лежать ссылка на объект. Копия структуры копирует ссылку, а не объект, поэтому обе копии правят одно и то же состояние, и семантика значения тут ничего не спасает.
Меняют структуру на класс из страха перед дорогим копированием. Класс приносит подсчёт ссылок, общее изменяемое состояние и риск цикла ссылок, а выигрыша может не быть вовсе: сначала замер, и только потом тяжёлое поле выносят в тип с копированием при записи.