Swift provides a lot of optimizations and swiftiness to our codebase and to the way how we can code and handle different cases. There are a lot of concepts, that help to make swiftiness real.

One of such concept - COW.

🐂 Not the one that eats grass and can give u milk, but a principle/idea that can be constructed from first letters - Copy-On-Write.

This principle provides a copy-on-write semantic for types that use it. This means, that we can get some boost in terms of performance and memory usage, even with a lot of elements in it.

The problem

Using values is fast and easy, but sometimes it may cost additional memory and performance.

// imagine we have a lot of elements in array
let foo = [1,2,3, ... , n] 
let bar = foo

If we simply perform a copy, we need additional memory for placing all the elements and additional time to do so, so performance will be lost.

But there are a lot of cases where u just need to pass an array (or another struct, array is just a sample) as a parameter to some function or for some iteration or to just have the same data in another obj. Do we need a copy in this case? The answer is no.

These cases are optimized and resolved thanks to copy-on-write semantics. This semantics can be used for any large value. The idea behind this is to do a copy only when we need it - when we edit the source.

That’s all the idea - not always create a copy of value for the variable that was used during assigning.

COW

Not all value types are coming with COW out of the box in Swift.

The COW is implemented as a feature for well-known data structures (array, dictionary, set) from the standard Swift library. This is done by Apple.

So, if u create a custom value type - this type doesn’t have any implementation related to COW. But u can add it if u need this.

As one of the samples how u can do this - check Apple advice

The king of the process is isKnownUniquelyReferenced(_:) function.

Example

As always, a good example is worth 1000 words. The best way to inspect how everything needs to be implemented - is to check a source code.

Let’s inspect URLRequest class from Foundation framework. If u do so, u can find next:

@available(*, deprecated, message: "Please use the struct type URLRequest")
public typealias MutableURLRequest = NSMutableURLRequest

public struct URLRequest : ReferenceConvertible, Equatable, Hashable {
    public typealias ReferenceType = NSURLRequest
    public typealias CachePolicy = NSURLRequest.CachePolicy
    public typealias NetworkServiceType = NSURLRequest.NetworkServiceType
    
    /*
     NSURLRequest has a fragile Ivar layout that prevents the swift subclass approach here, so instead, we keep an always mutable copy
    */
    internal var _handle: _MutableHandle<NSMutableURLRequest>
    
    internal mutating func _applyMutation<ReturnType>(_ whatToDo : (NSMutableURLRequest) -> ReturnType) -> ReturnType {
        if !isKnownUniquelyReferenced(&_handle) {
            let ref = _handle._uncopiedReference()
            _handle = _MutableHandle(reference: ref)
        }
        return whatToDo(_handle._uncopiedReference())
    }
    
    ...
}

function _applyMutation is the heart of the process. U can see, that under the hood, Swift use obj-C class NSMutableURLRequest as a storage:

internal var _handle: _MutableHandle<NSMutableURLRequest>

In case we modify something in the URLRequest struct copy, the copy of the backed object is created.

Custom type and COW

The steps that is needs to be done, to use COW in our custom type are next:

  • create a kind of storage - a place where u’r data will be stored.
  • wrap to a struct, and use this storage under the hood
  • on setting a new value / change, check with isKnownUniquelyReferenced(_:) to know if u needs to make a copy.

Here is a sample of data structure that uses COW - kind of FIFO.

The FIFO idea:

fifo-lifo.png



I didn’t implement all functions needed for real FIFO, because this is not important right now.

Click to see the code

struct FIFO {

  final class Storage<Element> {
    var items: [Element] = []

    // MARK: - Lifecycle

    init() {}

    private init(_ items: [Element])  {
      self.items = items
    }

    // MARK: - Internal

    func add(_ item: Element)  {
      items.append(item)
    }

    func fetch() -> Element? {
      if items.isEmpty {
        return nil
      } else {
        return items.remove(at: 0)
      }
    }

    // MARK: - Copy

    func copy() -> Storage<Element> {
      Storage<Element>(items)
    }
  }

  private var _fifo = FIFO.Storage<Int>()

  mutating private func checkUniquelyReferencedInternalQueue() {
    if isKnownUniquelyReferenced(&_fifo) {
      debugPrint("reuse")
    } else {
      debugPrint("copied")
      _fifo = _fifo.copy()
    }
  }

  public mutating func put(_ item: Int) {
    checkUniquelyReferencedInternalQueue()
    _fifo.add(item)
  }

  public mutating func take() -> Int? {
    checkUniquelyReferencedInternalQueue()
    return _fifo.fetch()
  }

  mutating public func uniquelyReferenced() -> Bool {
    isKnownUniquelyReferenced(&_fifo)
  }
}


If u test the code above, u will see a message

"copied"

as soon as u execute line fifo2.put(2).

Of cause, I can just use array as storage and this will give me COW for free, but the idea is to show the possible implementation of the COW in a custom data structure.

Resources