Как создать контейнер с семантикой копирования при записи? (Быстрый)

У меня очень большая структура, и я хочу убедиться, что ее не копируют без нужды. Как сделать для него контейнер для копирования при записи?


person Vatsal Manot    schedule 07.10.2015    source источник
comment
Какая у вас большая структура? Я спрашиваю, потому что Swift уже выполняет (часть) эту работу за вас. См. stackoverflow.com/questions/26593992/   -  person 0x416e746f6e    schedule 07.10.2015
comment
@AntonBronnikov: Я в курсе этих оптимизаций. Этот вопрос был задуман как учебное пособие по вопросам и ответам.   -  person Vatsal Manot    schedule 07.10.2015
comment
Я понимаю. Вот почему я спрашиваю о том, для какой большой конструкции она предназначена. Тот факт, что такие образовательные вопросы и ответы существуют, говорит о том, что в них есть необходимость (например, что, если не все варианты использования охвачены оптимизацией компилятора Swift?). Это только улучшило бы ситуацию, если бы вы могли упомянуть применимые варианты использования, поскольку в противном случае было бы пустой тратой усилий делать / поддерживать что-то, что Swift сделает для вас в любом случае и бесплатно.   -  person 0x416e746f6e    schedule 07.10.2015
comment
В Swift 4 используйте isKnownUniquelyReferenced вместо isUniquelyReferenced   -  person Dan Lee    schedule 22.06.2017


Ответы (3)


Копирование при записи обычно представляет собой struct оболочку над некоторым поддерживающим объектом.

public final class MutableHeapStore<T>: NonObjectiveCBase
{
    public typealias Storage = T

    public private(set) var storage: Storage

    public init(storage: Storage)
    {
        self.storage = storage
    }
}

public struct COW<T>
{
    public typealias Storage = MutableHeapStore<T>
    public typealias Value = T

    public var storage: Storage

    public init(storage: Storage)
    {
        self.storage = storage
    }

    public var value: Value
    {
        get
        {
            return storage.storage
        }

        set
        {
            if isUniquelyReferenced(&storage)
            {
                storage.storage = newValue
            }

            else
            {
                storage = Storage(storage: newValue)
            }
        }
    }

    public init(_ value: Value)
    {
        self.init(storage: Storage(storage: value))
    }
}

extension COW: CustomStringConvertible
{
    public var description: String
    {
        return String(value)
    }
}

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

Является ли этот код потокобезопасным? Он так же безопасен, как и любой другой тип значения, например Int или Bool.

person Vatsal Manot    schedule 07.10.2015
comment
похоже, что у кода в isUniquelyReferenced никогда не было шансов быть выполненным, я добавляю оператор печати внутри, никогда не получал вывода - person dispute; 26.02.2016

Вот более простой пример.

struct COWExample1<T> {
    private var box = Box<[T]>(value: [T]())
    var count: Int {
        return box.value.count
    }
    mutating func append(e: T) {
        ensureBoxUniqueRefed()
        box.value.append(e)
    }
    private mutating func ensureBoxUniqueRefed() {
        if isUniquelyReferencedNonObjC(&box) == false {
            box = box.duplicated()
        }
    }
}

private final class Box<T> {
    var value: T
    init(value: T) {
        self.value = value
    }
    func duplicated() -> Box<T> {
        return Box(value: value)
    }
}
person eonil    schedule 03.06.2016

Предыдущие ответы не ошибочны, но есть гораздо более простой способ. У команды Swift есть список советов по эффективности, и они говорят:

Самый простой способ реализовать копирование при записи - это составить существующие структуры данных с копированием при записи, такие как массив.

Нет ничего проще!

person Ssswift    schedule 30.04.2017
comment
Хороший совет, особенно для новичков. - person Vatsal Manot; 30.04.2017
comment
Не только для новичков - в этом документе специально сказано, что целевая аудитория этого документа - разработчики компиляторов и стандартных библиотек. - person Ssswift; 01.05.2017