3

I have a very large structure, which I want to ensure is not copied needlessly. How can I make a copy-on-write container for it?

Vatsal Manot
  • 17,695
  • 9
  • 44
  • 80
  • What kind of large structure do you have? I am asking because Swift already does (part of) the job for you. See http://stackoverflow.com/questions/26593992/when-does-the-copying-take-place-for-swift-value-types – 0x416e746f6e Oct 07 '15 at 07:23
  • @AntonBronnikov: I am aware of these optimizations. This question was meant to be an educational Q/A tutorial. – Vatsal Manot Oct 07 '15 at 08:27
  • I understand. That's why I ask about the kind of large structure this is intended for. The fact that such educational Q/A exists suggests that there is a need for it (e.g. what if not all use cases are covered by Swift compiler optimisations?). It would only put things into a better picture if you could mention applicable use cases, as otherwise it would be a waste of effort to do/maintain something that Swift would do for you anyway and at no cost. – 0x416e746f6e Oct 07 '15 at 10:18
  • In Swift 4, use `isKnownUniquelyReferenced` instead of `isUniquelyReferenced` – Dan Lee Jun 22 '17 at 02:35

3 Answers3

6

A copy-on-write is usually a struct wrapper over some backing object.

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)
    }
}

The trick lies in asserting isUniquelyReferenced every time the boxed value is mutated. If the underlying storage object is singly referenced, nothing is to be done. However if another reference exists, one must create a new storage.

Is this code thread-safe? It is exactly as safe as any other value type, e.g. Int or Bool.

Vatsal Manot
  • 17,695
  • 9
  • 44
  • 80
  • seem like the code in the `isUniquelyReferenced` never got chance be executed, I add a print statement inside, never got the output – dispute Feb 26 '16 at 15:21
3

The previous answers aren't wrong, but there's a much simpler way. The Swift team has a list of performance tips, and they say:

The easiest way to implement copy-on-write is to compose existing copy-on-write data structures, such as Array.

It doesn't get much simpler than that!

Ssswift
  • 916
  • 10
  • 20
2

Here's a bit simpler example.

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)
    }
}
eonil
  • 83,476
  • 81
  • 317
  • 516