Inout в быстром и справочном типе - PullRequest
0 голосов
/ 09 мая 2018

Я пытаюсь понять разницу между значением и типом ссылки. А теперь я хочу использовать функцию из руководства для яблок:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

и если я хочу использовать эту функцию, я напишу этот код

swapTwoInts{&firstIntStruct, &secondIntStruct}

Я понимаю, что мы должны добавить в эту функцию ссылочный тип, но Int является типом значения, поэтому мы используем &.
С другой стороны, когда я пытаюсь изменить Int на мой класс в функции swap, я также должен написать & перед экземпляром моего класса.

Почему я должен это делать, если это уже ссылка?

Ответы [ 3 ]

0 голосов
/ 09 мая 2018

Итак, есть несколько компонентов памяти более низкого уровня, которые в полной мере понимают это.

1) При создании значения или ссылочного типа в стеке появляется новая переменная. Эта переменная является либо фактическими данными в случае типа значения, либо указателем на данные в случае ссылочного типа.

2) Когда вы вызываете функцию, она создает новую часть стека и создает в стеке новые переменные (которые являются let экземплярами в swift), которые копируют переданную переменную. Так что для типов значений это делает глубокая копия и для ссылочных типов копирует указатель.

Так что это означает, что когда вы используете inout, который вы говорите, возьмите адрес памяти этой переменной и обновите данные, которые она содержит. Таким образом, вы можете либо присвоить типу значения новые данные, либо ссылочному типу новый адрес указателя, и он изменится вне области функции подкачки. Это делает его var (таким же, как то, что передается) вместо let, как обычно.

0 голосов
/ 09 мая 2018

Я хотел бы объяснить это на примере. Как упомянул @Alexander, для th-й функции с Int в качестве параметра:

1. Проход по значению

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

Более фундаментально, проблема заключается в том, что когда вы передаете ссылку (на экземпляр класса, который мы называем объектом) функции, сама ссылка копируется (она ведет себя как тип значения). Если эта функция изменяет значение ссылки, она, как мы видели, изменяет только свою локальную копию.

enter image description here

Вы можете видеть, что

func swapTwoInts(_ a: Int, _ b: Int) { }

Если изменились значения p и q , где self.x и self.y не изменены. Поскольку эта функция передает значения x и y , а не их ссылки.

2. Передайте по ссылке:

func swapTwoInts(_ a: inout Int, _ b: inout Int) { }

Он передает ссылку на self.x и self.y , и поэтому вам не нужно снова их мутировать, как при взятии p и q в предыдущем типе. Потому что он возьмет мутирующий объект с var x и var y.

Вы можете видеть, что a и b имеют значения ссылок в журналах, и изменение a и b также изменило self.x и self.y , поскольку a и b имеет ту же ссылку (адрес), что и x и y.

enter image description here

0 голосов
/ 09 мая 2018

Предположим, мы написали гипотетическую функцию, о которой вы говорите:

class C {}

func swapTwoC(_ lhs: C, rhs: C) {
    let originalLHS = lhs
    lhs = rhs
    rhs = originalLHS
}

Непосредственная проблема заключается в том, что lhs и rhs являются неизменяемыми. Чтобы изменить их, нам нужно сделать изменяемые копии:

func swapTwoC(_ lhs: C, rhs: C) {
    var lhs = lhs; var rhs = rhs
    let originalLHS = lhs
    lhs = rhs
    rhs = originalLHS
}

Но теперь проблема в том, что мы мутируем наши копии, а не оригинальные ссылки, которые дал нам вызывающий.

Более фундаментально, проблема заключается в том, что когда вы передаете ссылку (на экземпляр класса, который мы называем объектом) функции, сама ссылка копируется (она ведет себя как тип значения). Если эта функция изменяет значение ссылки, она, как мы видели, изменяет только свою локальную копию.

Когда у вас есть inout C, и вы передаете &myObject, то, что вы фактически передаете, является ссылкой на вашу ссылку на myObject. Когда аргументы функции копируются, то копируется этот «ref to ref». Затем функция может использовать этот «ref to ref», чтобы назначить новое значение для ссылки myObject, которую имеет вызывающий

...