Understanding Swift Copy-on-Write mechanisms

In Swift, we have reference types(Classes) and value types (Structs, Tuples, enums). The value types have a copy semantic. That means if you assign a value type to a variable or pass it as a parameter to a function(unless it is an inout parameter), the underlying data of this value is going to be copied. You will have two values with the same content but, allocated in two distinct memory addresses. For a more detailed explanation about the difference between reference and value types on Apple’s Blog.

Since we are going to talk about Copy-on-Write, is very important to understand the Swift value semantics.

So … Let’s start

What is this Copy-on-Write?

In Swift when you have large value type and have to assign or pass as a parameter to a function, copying it can be really expensive in terms of performance because you'll have to copy all the underlying data to another place in memory.

Trying to minimize the issue, the Swift Standard library implements this set of mechanisms for some value types such as Array, where the value will be copied only upon mutation, and even then, only if it has more than one reference to it, because if this value is uniquely referenced, it doesn’t need to copy, it can be just mutated on the reference. So, just assign to a variable or pass an Array to a function doesn’t necessarily means it’ll be copied and that really improve the performance.

Something really important to know is …

Copy-on-Write is not a default behavior of value types, is something that is implemented on the Swift Standard Library for certain types such as Array and Collections. So, it means that not every value type in the Standard Library has this behavior. Also, the value types that you create doesn’t have it, unless you implement it yourself. This is something I’ll talk about later in the next section.

Let's see in practice how works with an example:

This is a simple way to show how Copy-on-Write works. Basically creating the array1 with values and assign to array2, where because of Copy-on-Write it isn’t being copied so both point to the same address. So the array2 data is being copied only when we mutate it as you can see on lines 15–17.

Implementing Copy-on-Write behavior for your custom value types

You can implement Copy-on-Write behavior yourself to take advantage of this with your custom value types. The example below can be found on OptimizationTips.rst in the Swift main repo.

It’s a sample code showing how you use a reference type to implement copy-on-write for a generic value type T. Basically, it’s a wrapper that manages the reference type and just returns a new instance if the value is not uniquely referenced. Otherwise, it just mutates the value of the reference type.

Conclusion

Copy-on-Write is a very intelligent way to optimize copy of value types and that is a mechanism heavily used in Swift, even though most of the times we don’t see it explicitly because fortunately, it’s implementation is done for us on the Standard Library. But is definitely something good to know so we can do our daily code implementations in a way that take full advantage of this.

That’s all for this article, hope you like it :)

If you got some comment or question, please let me know. I will be really happy in receiving your feedback :)

You can find me on twitter at @LucianoPassos11

Thanks for reading :)