Swift: как закрытие фиксирует переменные типа значения?

Взгляните на следующий фрагмент кода

struct Person{
  var name: String
  let surname: String
  var closure: (()->())?
   init(name: String, surname: String){
      self.name = name
      self.surname = surname
    
   }
}

var person = Person(name: "John",  surname: "Lennon")
let cl = {
   print(person.name)
}

person.name = "Bill"
cl()
print(person.name)

вывод приведенного выше фрагмента

Bill
Bill

Может кто-нибудь объяснить, как это происходит? Я думал, что, поскольку замыкание является ссылочным типом, а Person является типом значения, то при создании замыкания оно получает свою собственную копию Person (поскольку типы значений копируются при проходе), поэтому изменение внешнего Person не должно влиять на Person, которое захвачено закрытие, но кажется, что это не работает таким образом. Я новичок в типах swift и value, поэтому, пожалуйста, не судите слишком строго мой вопрос.
Спасибо
P.S. Я знаю, что мы можем захватить переменную значения явно, используя список захвата, и в этом случае изменение внешней переменной не влияет на захваченную переменную. Вопрос не в этом. Вопрос о том, что я думал, что он должен быть таким же, даже без явного захвата


person jonnyB_5531    schedule 16.12.2020    source источник
comment
@JoakimDanielson ‹b›Это потому, что функции и замыкания являются ссылочными типами.‹/b› . Взято из официального документа Swift docs.swift.org/swift-book/LanguageGuide/Closures .html.   -  person jonnyB_5531    schedule 16.12.2020


Ответы (1)


Ожидаемое поведение работает только тогда, когда вы явно передаете переменную в замыкание следующим образом:

var person = Person(name: "John",  surname: "Lennon")
let cl: (Person) -> () = { person in
    print(person.name)
}
cl(person)
person.name = "Bill"
cl(person)

Когда вы неявно захватываете переменную в замыкании, эта переменная всегда передается по ссылке. Если вы хотите захватить переменные по значению, вам нужно явно передать их.

person Dávid Pásztor    schedule 16.12.2020
comment
Спасибо за ваш ответ. Не могли бы вы объяснить больше относительно that variable is always passed by reference. Как можно передать ref, если это тип значения? Означает ли это, что в этом случае тип значения как-то равен Boxed, чтобы передать ссылку? - person jonnyB_5531; 16.12.2020
comment
@jonnyB_5531 типы значений по-прежнему имеют адрес памяти, где они хранятся (и при передаче по ссылке по существу передается указатель на этот адрес памяти). Просто подумайте о том, как работают аргументы inout для типов значений. - person Dávid Pásztor; 16.12.2020
comment
Является ли «inout» правильным примером в этом случае? Насколько я знаю, «inout» следует правилам копирования-в-копировании, поэтому, когда переменная передается как inout, вы все равно получаете ее копию, которую можно изменить внутри функции, а затем присвоить той же ячейке памяти. - person jonnyB_5531; 16.12.2020