Ответ
Нет, не всегда происходит физическое копирование. Swift использует семантику копирования при присваивании, но применяет оптимизации для эффективности.
Ключевые моменты:
- Семантическое копирование: При присваивании (
let b = a) или передаче в функцию создается новый экземпляр с семантикой независимой копии. - Copy-on-Write (CoW): Для структур, хранящих данные в буфере (как
Array,String,Dictionary), физическое копирование буфера откладывается до момента модификации. Это оптимизация производительности. - Исключение
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)
Вот так-то, блядь. Думаешь, что всё просто и предсказуемо, а там под капотом целый театр абсурда с оптимизациями и исключениями. Главное — логика сохраняется, а как именно оно там копируется или не копируется — это уже детали реализации, на которые можно смотреть сквозь пальцы, пока всё работает быстро.