Чи є швидкий пропуск за вартістю або пропускний посилання


100

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

Чи пропуск за посиланням такий же, як і в Objective-C або Java, де ви фактично передаєте посилання "a" чи це правильний пропуск за посиланням?


"Чи пропуск за посиланням такий же, як у Objective-C або Java" Ні Objective-C, ні Java не мають посилання на посилання.
newacct

2
Так. Я це знаю. Ви не проходите шляхом посилання. Ви передаєте посилання за значенням. Я припускав, що це було відомо під час відповіді.
gran_profaci

Java передає значення, а не посилання.
6рхіда

Відповіді:


167

Типи речей у Свіфті

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

  • Екземпляри класів є типовими типами (тобто ваше посилання на екземпляр класу фактично є вказівником )

  • Функції - це еталонні типи

  • Все інше - це ціннісний тип ; "все інше" просто означає випадки структур і випадки перерахунків, тому що це все є у Свіфта. Масиви та рядки - це, наприклад, структурні екземпляри. Ви можете передавати посилання на одну з цих речей (як аргумент функції), використовуючи inoutта беручи адресу, як вказував newacct. Але тип сам по собі є типом значення.

Які типи посилань означають для вас

Об'єкт еталонного типу є спеціальним на практиці, оскільки:

  • Просто призначення або перехід до функції може дати кілька посилань на один і той же об'єкт

  • Сам об’єкт є змінним, навіть якщо посилання на нього є постійною ( letабо явною, або мається на увазі).

  • Мутація до об'єкта впливає на цей об'єкт, як видно з усіх посилань на нього.

Це може бути небезпекою, тому слідкуйте за цим. З іншого боку, передача еталонного типу очевидно ефективна, оскільки копіюється та передається лише покажчик, що є тривіальним.

Які типи значень означають для вас

Очевидно, що передача типу значення "безпечніше" і letозначає те, що воно говорить: ви не можете мутувати екземпляр структури або екземпляр enum черезlet посилання. З іншого боку, ця безпека досягається шляхом окремої копії значення, чи не так? Чи не робить це передачу типу значення потенційно дорогим?

Ну так і ні. Це не так погано, як ви могли подумати. Як сказав Нейт Кук, передача типу значення не обов'язково означає копіювання, тому що let(явне або мається на увазі) гарантує незмінність, тому не потрібно нічого копіювати. І навіть перехід до varпосилання не означає, що речі будуть скопійовані, лише те, що вони можуть бути при необхідності (тому що є мутація). Документи спеціально радять не стискати в'язані штани.


6
"Екземпляри класу передаються посиланням. Функції передаються посиланням" Ні. Це значення пропускання, коли параметр не inoutзалежить від типу. Чи є щось прохідним посиланням, є ортогональним для типів.
newacct

4
@newacct Ну, звичайно, ти маєш рацію в суворому розумінні! Строго слід сказати, що все є прохідним значенням, але, що екземпляри і екземпляри структури є типовими значеннями і що екземпляри та функції класу є типовими типами . Дивіться, наприклад, developer.apple.com/swift/blog/?id=10 - також дивіться developer.apple.com/library/ios/documentation/Swift/Conceptual/… Однак я думаю, що я сказав, що відповідає загальним сенс слів означають.
мат

6
Право та типи значень / типи посилань не слід плутати з прохідними значеннями / пропускними посиланнями, оскільки типи значень можуть передаватися за значенням або за посиланням, а типи посилань також можуть передаватися за значенням або за посиланням.
newacct

1
@newacct дуже корисна дискусія; Я переписую своє резюме, щоб не ввести його в оману.
мат

43

Це завжди прохідне значення, коли параметр не єinout .

Якщо параметр є, це завжди проходить посиланняinout . Однак це дещо ускладнюється тим фактом, що вам потрібно явно використовувати &оператор на аргументі при переході до inoutпараметра, тому він може не відповідати традиційному визначенню проходження посилання, де ви передаєте змінну безпосередньо.


3
Ця відповідь у поєднанні з Нейт Куком була для мене зрозумілішою (походить від C ++) з приводу того, що навіть "тип посилання" не буде змінено поза межами функції, якщо ви чітко не вкажете її (використовуючи inout)
Gobe

10
inoutнасправді не передається посиланням, а копіюється після копіювання. Це лише гарантує, що після виклику функції змінене значення буде присвоєно вихідному аргументу. Параметри
виводу

хоча правда, що все передається за значенням. Властивості типів посилань можуть бути змінені всередині функції, як і копії посилань на той самий екземпляр.
MrAn3

42

Все в 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, оскільки додає коду зайву складність. Найкраще взяти параметри та повернути один результат. Здійснюючи це, зазвичай сигналізує про поганий дизайн. Інший спосіб сказати, що приховані побічні ефекти у переданих посиланнях змінних не є прозорими для абонента.
Кріс Амелінккс

Це не проходить через посилання. inoutє оператором копіювання, копіювання. Спочатку він буде скопійовано в об’єкт, а потім замінить початковий об’єкт після повернення функції. Хоча це може здатися однаковим, існують тонкі відмінності.
Ганнес Гертах

7

У блозі розробників Apple Swift є посада під назвою Цінні та референтні типи яка забезпечує чітке та детальне обговорення цієї самої теми.

Цитувати:

Типи Swift поділяються на одну з двох категорій: по-перше, «типи значень», де кожен екземпляр зберігає унікальну копію своїх даних, зазвичай визначається як структура, перерахунок або кортеж. Другий, "типи посилань", де екземпляри діляться однією копією даних, а тип зазвичай визначається як клас.

Публікація в блозі Swift продовжує пояснювати відмінності на прикладах та пропонує коли ви будете використовувати один над іншим.


1
Це не дає відповіді на запитання. Питання стосується пропускної вартості проти проходження посилання, яка є повністю ортогональною для типів значень порівняно з типовими типами.
Йорг W Міттаг

2

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


Це неправильно. inoutє оператором копіювання, копіювання. Спочатку він буде скопійовано в об’єкт, а потім замінить початковий об’єкт після повернення функції. Хоча це може здатися однаковим, існують тонкі відмінності.
Ганнес Гертах

2

Якщо ви використовуєте inout з оператором infix, наприклад + =, символ & address може ігноруватися. Я думаю, компілятор передбачає пройти за посиланням?

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

origDictionary + = newDictionaryToAdd

І добре, що цей словник "додати" лише одне записування до оригіналу посилання, і це чудово для блокування!


2

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

Однією з найважливіших відмінностей між структурами та класами є те, що структури завжди копіюються, коли вони передаються навколо у вашому коді, але класи передаються за посиланням.

Закриття

Якщо ви призначите закриття властивості екземпляра класу, а закриття захоплює цей екземпляр, посилаючись на екземпляр або його членів, ви створите міцний контрольний цикл між закриттям і екземпляром. Swift використовує списки захоплення для розбиття цих сильних еталонних циклів

ARC (автоматичний підрахунок посилань)

Підрахунок посилань стосується лише екземплярів класів. Структури та перерахування є типовими значеннями, а не типовими типами і не зберігаються та передаються посиланням.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.