Что такое value types

Value types (типы значений) — это типы данных, которые при присваивании или передаче в функцию копируются, а не передаются по ссылке. Каждый экземпляр типа значения обладает собственной независимой копией данных. Изменения, сделанные с одной копией, не влияют на другие.

Этот механизм противопоставляется reference types (типам ссылок), которые передаются по ссылке, и все переменные, указывающие на один и тот же объект, разделяют одно и то же состояние.

📦 Основные характеристики value types

  1. Передача по значению: при присваивании переменной или при передаче в функцию создаётся новая копия данных.

  2. Отсутствие идентичности (identity): объекты не обладают уникальностью; сравнение выполняется по содержимому.

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

  4. Иммутабельность с let: если переменная-значение объявлена как let, она становится полностью неизменяемой, включая её внутренние свойства.

  5. Обычно размещаются в стеке: хотя на практике многие value types используют оптимизации и могут быть размещены в куче (особенно при использовании Copy-on-Write).

📚 Примеры value types в Swift

  • Int, Float, Double

  • Bool, Character

  • String

  • Array, Dictionary, Set

  • Enum (перечисления)

  • Struct (структуры)

  • Кортежи (tuples)

Даже такие, казалось бы, «тяжёлые» структуры как Array или String — являются value types, благодаря оптимизациям на уровне Copy-on-Write.

🧬 Поведение при копировании

struct Point {
var x: Int
var y: Int
}
var a = Point(x: 0, y: 0)
var b = a // создаётся копия
b.x = 10
print(a.x) // 0
print(b.x) // 10

В этом примере a и b — это два разных объекта. Изменения в b не влияют на a.

🔁 Value vs Reference: ключевые отличия

Характеристика Value Type Reference Type
Передача По значению (копия) По ссылке
--- --- ---
Идентичность (===) Нет Есть
--- --- ---
Поведение при изменении Независимо Влияет на все ссылки
--- --- ---
Место хранения Обычно стек Куча
--- --- ---
Примеры Struct, Enum, Array, Int Class, NSObject, UIView
--- --- ---

🔒 Value types и безопасность данных

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

Особенно это актуально в контексте immutable data (неизменяемых структур), которые гарантируют, что состояние объекта не может быть изменено после инициализации. Это повышает устойчивость к багам, связанным с состоянием (state).

🧠 Copy-on-Write (COW)

Хотя многие value types (например, Array, String) копируются при передаче, Swift использует оптимизацию копирования при записи:

  • Пока вы не изменяете данные, копия не создаётся.

  • Как только происходит попытка изменения, создаётся реальная копия (если ссылка не единственная).

var a = \[1, 2, 3\]
var b = a // пока только ссылка на те же данные
b.append(4) // теперь b становится отдельной копией
print(a) // \[1, 2, 3\]
print(b) // \[1, 2, 3, 4\]

Swift отслеживает, сколько ссылок у массива (через reference counter внутри реализации), и клонирует только при необходимости.

🧰 Использование value types в архитектуре

Value types активно используются в:

  • SwiftUI: вся архитектура декларативного UI строится на value types — View в SwiftUI — это протокол, реализуемый структурой.

  • Моделировании данных: например, структуры User, Product, Settings.

  • Functional Programming: чистые функции + неизменяемые значения.

Пример:

struct User {
let id: Int
var name: String
}
let originalUser = User(id: 1, name: "Alice")
var modifiedUser = originalUser
modifiedUser.name = "Bob"
// originalUser остаётся неизменным

📌 Mutating methods

Поскольку value types копируются, внутри структур Swift требует явного указания, что метод изменяет внутренние свойства.

struct Counter {
var count = 0
mutating func increment() {
count += 1
}
}

Без mutating компилятор выдаст ошибку при попытке изменить count.

📎 Когда использовать value types

Используйте value types, когда:

  • Вам нужно копируемое и независимое состояние.

  • Вы хотите предсказуемое поведение при передаче между компонентами.

  • Вы проектируете модель данных, которая не зависит от идентичности.

  • Вам важно отсутствие shared state, особенно в многопоточных сценариях.

  • Вы хотите гарантировать, что объект нельзя случайно изменить извне.

🌐 Value types в других языках

C++:

  • int, float, struct — всё value.

  • std::string и std::vector используют COW или move-семантику.

C#:

  • struct — value type.

  • class — reference type.

  • Имеется чёткое разделение между stack и heap.

Go:

  • Все struct передаются по значению по умолчанию.

  • Использование указателей (*Type) нужно явно.

Java:

  • Все объекты — reference type.

  • Только примитивы (int, boolean, double) — value type.

  • Value types планируются к полноценной поддержке через Project Valhalla.

📦 Сравнение с reference types в Swift

struct UserStruct {
var name: String
}
class UserClass {
var name: String
}
var structA = UserStruct(name: "Alex")
var structB = structA
structB.name = "Bob" // только structB изменился
var classA = UserClass()
classA.name = "Alex"
var classB = classA
classB.name = "Bob" // classA тоже изменился

Именно такое поведение делает value types удобными для безопасного управления состоянием.