Как реализовать Copy-on-Write (CoW) для пользовательской структуры в Swift?

Ответ

Да, можно вручную реализовать семантику Copy-on-Write для кастомной структуры, чтобы оптимизировать производительность при работе с большими значениями.

Принцип работы: Данные хранятся в ссылочном типе (классе), а копирование буфера происходит только при попытке его изменения (записи), если на него есть несколько ссылок.

Пример реализации:

struct MyCoWStruct {
    // 1. Данные хранятся в приватном классе
    private class Storage {
        var data: [Int]
        init(data: [Int]) { self.data = data }
    }

    // 2. Единственная ссылка на хранилище
    private var storage: Storage

    init(data: [Int]) {
        storage = Storage(data: data)
    }

    // 3. Публичное свойство с логикой CoW
    var elements: [Int] {
        get { storage.data }
        set {
            // 4. Ключевая проверка: копируем только если хранилище разделяется
            if !isKnownUniquelyReferenced(&storage) {
                storage = Storage(data: storage.data)
            }
            storage.data = newValue
        }
    }

    // 5. Мутирующий метод также должен проверять уникальность ссылки
    mutating func append(_ value: Int) {
        if !isKnownUniquelyReferenced(&storage) {
            storage = Storage(data: storage.data)
        }
        storage.data.append(value)
    }
}

// Использование
var cow1 = MyCoWStruct(data: [1, 2, 3])
var cow2 = cow1 // На этом этапе копирования данных НЕТ

cow2.append(4) // Тут срабатывает CoW: создается новый Storage для cow2
print(cow1.elements) // [1, 2, 3]
print(cow2.elements) // [1, 2, 3, 4]

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

  • isKnownUniquelyReferenced(&object) — проверяет, является ли ссылка на объект уникальной.
  • Все мутирующие операции должны начинаться с этой проверки.
  • Стандартные типы Swift (Array, Dictionary, String) уже используют CoW под капотом.

Ответ 18+ 🔞

Ага, слушай, вот это тема! Смотри, бывает же такое — у тебя здоровенная структура, как шкаф советский, и её туда-сюда копировать — это ж пиздец просто, производительность на ноль ебать. Так вот, можно сделать свою Copy-on-Write логику, это когда данные копируются только тогда, когда ты реально лезешь их менять, а не просто так, от нехуй делать.

Как это работает, блядь: Суть в том, что все твои жирные данные засовываются в ссылочный тип, в класс, понимаешь? А сама структура — это просто контейнер с одной ссылкой на эту хуйню. И когда ты делаешь присваивание, копируется только ссылка, а не тонны данных. А вот когда ты начинаешь писать в эту структуру — вот тут-то и проверяем: а не смотрит ли ещё кто-то на этот же буфер? Если да — тогда, ёпта, делаем полную копию, чтобы не наебать соседа.

Смотри, как это выглядит в коде, вротберунчик:

struct MyCoWStruct {
    // 1. Прячем данные в приватный класс, как золото в сейф
    private class Storage {
        var data: [Int]
        init(data: [Int]) { self.data = data }
    }

    // 2. У нас тут одна-единственная пиздюлина-ссылка на хранилище
    private var storage: Storage

    init(data: [Int]) {
        storage = Storage(data: data)
    }

    // 3. Публичное свойство, где вся магия и происходит
    var elements: [Int] {
        get { storage.data }
        set {
            // 4. Вот она, проверка на уникальность! Если на хранилище смотрят больше одной пары глаз — копируем!
            if !isKnownUniquelyReferenced(&storage) {
                storage = Storage(data: storage.data)
            }
            storage.data = newValue
        }
    }

    // 5. Любой метод, который меняет данные, должен начинаться с той же ебалы
    mutating func append(_ value: Int) {
        if !isKnownUniquelyReferenced(&storage) {
            storage = Storage(data: storage.data)
        }
        storage.data.append(value)
    }
}

// Пример использования, чтобы не быть мудаком
var cow1 = MyCoWStruct(data: [1, 2, 3])
var cow2 = cow1 // В этот момент — нихуя не копируется! Только ссылка.

cow2.append(4) // А вот тут — бац! Проверка, копирование, и только потом запись.
print(cow1.elements) // [1, 2, 3] — оригинал не тронут, красота!
print(cow2.elements) // [1, 2, 3, 4] — у копии своя жизнь.

На что смотреть, ёпта:

  • isKnownUniquelyReferenced(&object) — это наше всё. Функция, которая говорит: «Чувак, на этот объект больше никто не ссылается?». Если нет — можно смело мутить.
  • Любое действие, которое меняет внутреннее состояние, должно с неё начинаться. Иначе получишь не CoW, а какую-то общую помойку, где все друг другу портят данные.
  • Кстати, стандартные штуки в Swift — Array, Dictionary, String — они уже так и работают под капотом, хитрожопые. Но если тебе своё, кастомное, надо — вот тебе схема.