← Назад к вопросам

Коллекции — это структуры или классы в Swift?

1.3 Junior🔥 191 комментариев
#Язык Swift

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Коллекции (Collections) в Swift: структуры или классы

Ответ: СТРУКТУРЫ (Value Types)

Все встроенные коллекции в Swift — Array, Dictionary, Set — это структуры (value types), а не классы (reference types).

Array — Структура

struct Array<Element> {
    // Реальная реализация намного сложнее
    // Но по поведению это value type
}

var array1 = [1, 2, 3]
var array2 = array1  // Копирование значения

array2.append(4)

print(array1)  // [1, 2, 3]
print(array2)  // [1, 2, 3, 4]
// Независимые копии!

Dictionary — Структура

struct Dictionary<Key, Value> where Key: Hashable {
    // Value type
}

var dict1: [String: Int] = ["a": 1, "b": 2]
var dict2 = dict1  // Копирование

dict2["c"] = 3

print(dict1)  // ["a": 1, "b": 2]
print(dict2)  // ["a": 1, "b": 2, "c": 3]

Set — Структура

struct Set<Element> where Element: Hashable {
    // Value type
}

var set1: Set<Int> = [1, 2, 3]
var set2 = set1  // Копирование

set2.insert(4)

print(set1)  // [1, 2, 3]
print(set2)  // [1, 2, 3, 4]

Почему структуры?

1. Copy-on-Write (CoW) оптимизация

// Swift оптимизирует копирование
var arr1 = [1, 2, 3]
var arr2 = arr1  // На самом деле не копирует данные немедленно

// Только когда модифицируем:
arr2.append(4)  // Вот здесь создается копия

// До модификации arr1 и arr2 делили один буфер в памяти
// Это очень эффективно!

2. Потокобезопасность

// Структура безопаснее в многопоточности
class ArrayWrapper {
    var array = [1, 2, 3]
}

let wrapper = ArrayWrapper()  // Reference type
var arr1 = wrapper.array      // Copy
var arr2 = wrapper.array      // Еще одна copy

// wrapper.array может быть изменен из другого потока
// Но arr1 и arr2 безопасны (это копии)

// Со структурой:
var array = [1, 2, 3]
var copy = array  // Copy (безопасно)

3. Предсказуемость

// С классом нужно постоянно думать о ссылках
class MutableArray {
    var items: [Int] = []
}

let arr1 = MutableArray()
let arr2 = arr1  // arr1 и arr2 указывают на ОДН объект
arr2.items.append(1)
print(arr1.items)  // [1] (изменилось!)

// Со структурой все ясно
var arr1 = [Int]()
var arr2 = arr1  // Копия
arr2.append(1)
print(arr1)  // [] (не изменилось)

Сравнение Value Type vs Reference Type

// VALUE TYPE (Array, Dictionary, Set, Struct)
var valueArray1 = [1, 2, 3]
var valueArray2 = valueArray1
valueArray2.append(4)
// valueArray1 остался [1, 2, 3]
// valueArray2 теперь [1, 2, 3, 4]

// REFERENCE TYPE (Class)
class ReferenceArray {
    var items = [1, 2, 3]
}

let refArray1 = ReferenceArray()
let refArray2 = refArray1  // Указывает на ТОТ ЖЕ объект
refArray2.items.append(4)
// refArray1.items = [1, 2, 3, 4]
// refArray2.items = [1, 2, 3, 4]
// Это ОДИН объект в памяти

Практические последствия

1. Mutating функции в структурах

struct Stack {
    private var elements: [Int] = []
    
    // Нужен mutating, потому что это структура
    mutating func push(_ value: Int) {
        elements.append(value)  // Изменяет self
    }
}

var stack = Stack()
stack.push(1)  // Работает

let immutableStack = Stack()
immutableStack.push(2)  // Ошибка: let не может быть mutated

// С классом
class ClassStack {
    private var elements: [Int] = []
    
    // Не нужен mutating
    func push(_ value: Int) {
        elements.append(value)  // Изменяет внутреннее состояние
    }
}

let classStack = ClassStack()
classStack.push(1)  // Работает! (потому что это reference type)

2. Производительность

// Структура дешево копируется благодаря CoW
let largeArray = Array(1...1000000)
let copy = largeArray  // Практически бесплатно (до модификации)

// Класс всегда копирует reference
class LargeArrayWrapper {
    var items = Array(1...1000000)
}

let obj1 = LargeArrayWrapper()
let obj2 = obj1  // Копирует только reference (8 байт)
// Но если изменим obj2.items - это создаст копию массива

3. Передача в функции

// Структура (копируется)
func processArray(_ arr: [Int]) {
    var mutableArray = arr  // Копия (CoW оптимизирует)
    mutableArray.append(999)
}

var original = [1, 2, 3]
processArray(original)
print(original)  // [1, 2, 3] (не изменилось)

// Класс (передается reference)
func processClass(_ obj: LargeArrayWrapper) {
    obj.items.append(999)  // Изменяет исходный объект
}

let obj = LargeArrayWrapper()
obj.items = [1, 2, 3]
processClass(obj)
print(obj.items)  // [..., 999] (изменилось!)

Почему Apple выбрала структуры

// Правило: Value Semantics (семантика значений)
// Коллекции представляют ДАННЫЕ, не сущности

let numbers = [1, 2, 3]  // Это данные
let copy = numbers        // Логично, что это копия

let user = User(name: "John")  // Это сущность
let another = user               // Нужна ли копия?
// Возможно, мы хотим ссылку на одного пользователя

// Поэтому:
// - Коллекции → struct (value type)
// - Domain Models → class или struct (в зависимости от контекста)

Сравнение с другими языками

Пython:   list = reference type (class)
JavaScript: Array = reference type (object)
Rust:     Vec = owns data (value type, но no copy)
Swift:    Array = value type (with CoW optimization)

Когда использовать какой тип

// Для коллекций — ВСЕГДА структуры, это встроено
let array = [1, 2, 3]  // Array<Int> struct
let dict = ["key": "value"]  // Dictionary struct
let set = Set([1, 2, 3])  // Set struct

// Для моделей данных — обычно структуры
struct Person {
    let name: String
    let age: Int
}

// Для объектов с поведением — часто классы
class DatabaseConnection {
    func connect() {}
    func query(_ sql: String) {}
}

Практический пример

// Задача: работать с коллекцией пользователей

struct User {
    let id: Int
    let name: String
}

var users = [User(id: 1, name: "John")]
var backup = users  // Копия (значение)

users.append(User(id: 2, name: "Jane"))

print(users)    // [John, Jane]
print(backup)   // [John] — независимая копия

// Это безопасно и предсказуемо!

Выводы

  • Array, Dictionary, Set — структуры (value types)
  • Copy-on-Write оптимизирует копирование
  • Потокобезопаснее чем reference types
  • Семантика значений — коллекции это данные
  • Mutating нужен для изменения структур
  • Reference equality (===) не работает для коллекций (используй ==)