Всегда ли value type в Swift копируется при присваивании?

Ответ

Нет, не всегда происходит физическое копирование. Swift использует семантику копирования при присваивании, но применяет оптимизации для эффективности.

Ключевые моменты:

  1. Семантическое копирование: При присваивании (let b = a) или передаче в функцию создается новый экземпляр с семантикой независимой копии.
  2. Copy-on-Write (CoW): Для структур, хранящих данные в буфере (как Array, String, Dictionary), физическое копирование буфера откладывается до момента модификации. Это оптимизация производительности.
  3. Исключение inout: При передаче value type как inout параметра, в функцию передается ссылка на оригинальный экземпляр, а не его копия.

Примеры:

struct Point { var x = 0, y = 0 }

// 1. Семантическое копирование
var p1 = Point(x: 1, y: 2)
var p2 = p1 // p2 — семантическая копия p1
p2.x = 3    // Изменяет только p2
print(p1.x) // 1 (p1 не изменился)

// 2. Copy-on-Write на практике
var array1 = [1, 2, 3]
var array2 = array1 // Буфер данных НЕ копируется здесь (оба массива ссылаются на один буфер)
array2.append(4)    // Только теперь происходит физическое копирование буфера для array2

// 3. Передача по ссылке с inout
func translate(point: inout Point) {
    point.x += 10 // Модифицирует оригинальную переменную
}
translate(point: &p1)
print(p1.x) // 11

Ответ 18+ 🔞

А, ну ты про это, про копирование value types в Swift! Да, тут, блядь, не всё так однозначно, как кажется на первый взгляд. Сидишь, думаешь: «О, структура, при присваивании скопируется, всё просто». А потом бац — и производительность на уровне ссылочных типов, ёпта! Как так-то?

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

Но под капотом-то, сука, сидит хитрая жопа по имени Copy-on-Write (CoW). Это как раз для тех самых структур, которые данные в буфере хранят: Array, String, Dictionary. Пока ты только читаешь — все «копии» тихо-мирно ссут в один горшок, то есть используют общий буфер. Экономия, блядь, овердохуища!

Но стоит одной из этих мартышлюшек взять и попытаться что-то изменить — вжух! — тут же происходит реальное, физическое копирование буфера, и она уже гадит в свой собственный. Вот и вся магия, в рот меня чих-пых.

А ещё есть исключение, которое всё ставит с ног на голову — параметр inout. Это когда ты говоришь: «Да похуй на копии, дай мне с оригиналом поработать!». И функция получает прямую ссылку, как с классами. Модифицируешь — и оригинал меняется нахуй. Осторожно, а то сам от себя охуеешь.

Вот, смотри на примерах, как этот цирк работает:

struct Point { var x = 0, y = 0 }

// 1. Та самая семантика копирования (как в учебнике)
var p1 = Point(x: 1, y: 2)
var p2 = p1 // p2 — семантическая копия p1, думаем, что скопировалось всё
p2.x = 3    // Меняем только p2
print(p1.x) // 1 (p1 остался старым, ура, логика не сломана)

// 2. А вот CoW в дикой природе (где реальная магия)
var array1 = [1, 2, 3]
var array2 = array1 // На этом этапе нихуя не копируется! Обе переменные тычут пальцем в одну кучу данных.
array2.append(4)    // А ВОТ ТУТ! Только сейчас, когда array2 захотела нагадить, для неё отгородили отдельный сортир и скопировали туда данные.

// 3. Исключение inout (полный беспредел)
func translate(point: inout Point) {
    point.x += 10 // Работаем напрямую с тем, что передали, а не с копией
}
translate(point: &p1) // Передаём оригинал p1 по ссылке
print(p1.x) // 11 (ого, а p1-то изменился! Вот тебе и value type)

Вот так-то, блядь. Думаешь, что всё просто и предсказуемо, а там под капотом целый театр абсурда с оптимизациями и исключениями. Главное — логика сохраняется, а как именно оно там копируется или не копируется — это уже детали реализации, на которые можно смотреть сквозь пальцы, пока всё работает быстро.