Быстрая передача по значению или передача по ссылке

100

Я новичок в Swift и только что прочитал, что классы передаются по ссылке, а массивы / строки и т. Д. Копируются.

Проход по ссылке такой же, как в Objective-C или Java, где вы фактически передаете ссылку, или это правильный проход по ссылке?

gran_profaci
источник
«Проходит по ссылке так же, как в Objective-C или Java» Ни в Objective-C, ни в Java нет передачи по ссылке.
newacct
2
Да. Я знаю это. Вы не проходите по ссылке. Вы передаете ссылку по значению. Я предположил, что это было известно, когда отвечал.
gran_profaci
Java передается по значению, а не по ссылке.
6rchid

Ответы:

167

Типы вещей в Swift

Правило такое:

  • Экземпляры класса являются ссылочными типами (т.е. ваша ссылка на экземпляр класса фактически является указателем )

  • Функции являются ссылочными типами

  • Все остальное тип значения ; «все остальное» просто означает экземпляры структур и экземпляры перечислений, потому что это все, что есть в Swift. Например, массивы и строки являются экземплярами структур. Вы можете передать ссылку на одну из этих вещей (в качестве аргумента функции), используя inoutи взяв адрес, как указал newacct. Но тип сам по себе является типом значения.

Что для вас значат ссылочные типы

На практике объект ссылочного типа является особенным, потому что:

  • Простое присвоение или передача функции может дать несколько ссылок на один и тот же объект.

  • Сам объект является изменяемым, даже если ссылка на него является константой (let явной или подразумеваемой).

  • Мутация объекта влияет на этот объект с точки зрения всех ссылок на него.

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

Какие типы значений значат для вас

Ясно, что передача типа значения «безопаснее» и letозначает то, о чем говорится: вы не можете изменить экземпляр структуры или экземпляр перечисления черезlet ссылки. С другой стороны, безопасность достигается созданием отдельной копии значения, не так ли? Разве это не делает передачу типа значения потенциально дорогостоящей?

Ну да и нет. Это не так плохо, как вы думаете. Как сказал Нейт Кук, передача типа значения не обязательно подразумевает копирование, потому что let(явное или подразумеваемое) гарантирует неизменность, поэтому нет необходимости что-либо копировать. И даже переход в varссылку не означает, что вещи будут скопированы, только то, что они могут быть при необходимости (потому что есть мутация). В документации особо не советуют выкручивать трусики.

матовый
источник
6
«Экземпляры классов передаются по ссылке. Функции передаются по ссылке» Нет. Это передача по значению, когда параметр не зависит inoutот типа. Является ли что-то передаваемым по ссылке, ортогонально типам.
newacct
5
@newacct Ну конечно в строгом смысле слова ты прав! Строго говоря, следует сказать, что все передается по значению, но что экземпляры перечисления и экземпляры структур являются типами значений, а экземпляры классов и функции являются ссылочными типами . См., Например, developer.apple.com/swift/blog/?id=10 - также см. Developer.apple.com/library/ios/documentation/Swift/Conceptual/. Однако я думаю, что то, что я сказал, согласуется с общим смысл слов значит.
Мэтт
6
Правильно, и типы значений / ссылочные типы не следует путать с передачей по значению / передачей по ссылке, потому что типы значений могут передаваться по значению или по ссылке, а ссылочные типы также могут передаваться по значению или по ссылке.
newacct
1
@newacct очень полезное обсуждение; Я переписываю свое резюме, чтобы оно не вводило в заблуждение.
Мэтт
@newacct Я все еще считаю ваш комментарий несколько вводящим в заблуждение. Средний человек, читающий это, здесь, потому что он не эксперт по Swift. Простое заявление о том, что все передается по значению, сбивает с толку и не проясняет, например, что можно передать ссылочный тип по ссылке, тем самым указывая на указатель, что, скорее всего, не входило в намерения разработчика. Оригинальный ответ Мэтта уместен.
Адриан Бартоломью,
44

Это всегда передается по значению, если параметр неinout .

Если параметр имеет значение, он всегда передается по ссылкеinout . Однако это несколько усложняется тем фактом, что вам нужно явно использовать &оператор для аргумента при передаче в inoutпараметр, поэтому он может не соответствовать традиционному определению передачи по ссылке, где вы передаете переменную напрямую.

newacct
источник
3
Этот ответ в сочетании с ответом Нейта Кука был для меня более ясным (исходя из C ++) в том, что даже «ссылочный тип» не будет изменен за пределами области действия функции, если вы явно не укажете его (с помощью inout)
Gobe
10
inoutна самом деле не передается по ссылке, а только с копированием. Он гарантирует только то, что после вызова функции измененное значение будет присвоено исходному аргументу. In-Out Параметры
Dalija Prašnikar
1
хотя правда, что все проходит по стоимости. Свойства ссылочных типов могут быть изменены внутри функции, поскольку копия ссылается на один и тот же экземпляр.
MrAn3
43

Все в Swift по умолчанию передается "копией", поэтому, когда вы передаете тип значения, вы получаете копию значения, а когда вы передаете ссылочный тип, вы получаете копию ссылки со всем, что подразумевается. (То есть копия ссылки по-прежнему указывает на тот же экземпляр, что и исходная ссылка.)

Я использую пугающие кавычки вокруг «копии» выше, потому что Swift много оптимизирует; везде, где это возможно, он не копируется, пока не произойдет мутация или возможность мутации. Поскольку параметры по умолчанию неизменяемы, это означает, что в большинстве случаев копирования фактически не происходит.

Нейт Кук
источник
Для меня это лучший ответ, поскольку он пояснил, что, например, свойства экземпляра могут быть изменены внутри функции, даже если параметр является копией (передача по значению), потому что он указывает на ту же ссылку.
MrAn3
Лучший ответ.
Адриан Бартоломью,
9

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

func ComputeSomeValues(_ value1: inout String, _ value2: inout Int){
    value1 = "my great computation 1";
    value2 = 123456;
}

Назовите это так

var val1: String = "";
var val2: Int = -1;
ComputeSomeValues(&val1, &val2);
Крис Амелинкс
источник
1
Почему вам следует избегать этого?
Brainless,
1
@Brainless, потому что это добавляет ненужной сложности коду. Лучше всего принимать параметры и возвращать один результат. Необходимость этого обычно свидетельствует о плохом дизайне. Другими словами, скрытые побочные эффекты в переданных переменных, на которые есть ссылки, не прозрачны для вызывающего.
Крис Амелинкс,
Это не проходит по ссылке. inoutэто оператор копирования в, копирование из. Сначала он скопирует объект, а затем перезапишет исходный объект после возврата из функции. Хотя это может показаться одинаковым, между ними есть небольшие различия.
Ханнес Гертах,
7

В блоге Apple Swift Developer есть пост под названием Value and Reference Types. котором содержится четкое и подробное обсуждение этой самой темы.

Цитировать:

Типы в Swift делятся на две категории: во-первых, «типы значений», где каждый экземпляр хранит уникальную копию своих данных, обычно определяемую как структура, перечисление или кортеж. Второй, «ссылочные типы», когда экземпляры совместно используют одну копию данных, а тип обычно определяется как класс.

Сообщение в блоге Swift продолжает объяснять различия с примерами и предлагает, когда вы могли бы использовать одно вместо другого.

почему
источник
1
Это не отвечает на вопрос. Вопрос касается передачи по значению и передачи по ссылке, которая полностью ортогональна типам значений и ссылочным типам.
Jörg W Mittag
2

По умолчанию классы передаются по ссылкам, а другие по значению. Вы можете передать по ссылке, используя inoutключевое слово.

Бахрам Зангене
источник
Это неверно. inoutэто оператор копирования в, копирование из. Сначала он скопирует объект, а затем перезапишет исходный объект после возврата из функции. Хотя это может показаться одинаковым, между ними есть небольшие различия.
Ханнес Гертах
2

Когда вы используете inout с инфиксным оператором, например + =, то символ адреса & можно игнорировать. Полагаю, компилятор предполагает передачу по ссылке?

extension Dictionary {
    static func += (left: inout Dictionary, right: Dictionary) {
        for (key, value) in right {
            left[key] = value
        }
    }
}

origDictionary + = newDictionaryToAdd

И хорошо, что этот словарь 'add' записывает только одну запись в исходную ссылку, так что отлично подходит для блокировки!

Жюль Берт
источник
2

Классы и структуры

Одно из наиболее важных различий между структурами и классами заключается в том, что структуры всегда копируются, когда они передаются в вашем коде, но классы передаются по ссылке.

Закрытие

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

ARC (автоматический подсчет ссылок)

Подсчет ссылок применяется только к экземплярам классов. Структуры и перечисления являются типами значений, а не ссылочными типами, и не хранятся и не передаются по ссылке.

Mohsen
источник