Hello ladies and gentlemen, Leo here. The day has come and we will talk about Copy-on-write in Swift. Yes, this is one of the most important features of the Swift programming language and all iOS developers should know what and how this happens under the hood.

First, we will introduce the differences between value types and reference types, after that, I’ll show how to check the memory address and see the mechanism of copy-on-write working. No more talking, let’s dive in.

Image explanation: copyist in the art scene is an ancient tradition that continues until today, since today’s subject is copying feature, I thought it would be a good reference.

 

Value types Vs Reference types – Copy-on-write in Swift

As Apple’s Swift blog explains we can define it like this:

Types in Swift fall into one of two categories: first, “value types”, where each instance keeps a unique copy of its data, usually defined as a struct, enum, or tuple. The second, “reference types”, where instances share a single copy of the data, and the type is usually defined as a class.

And we can see this particular behavior when copying variables. Check below the examples.

 

Value Type Example

Let’s explore how a value type, a struct in this case behaves when you assign it to another new variable.

func address(o: UnsafeRawPointer) -> Int {
    return Int(bitPattern: o)
}

struct Car { var name: Int = -1 }
var a = Car()
var b = a // a is copied to b
a.name = 42 // Changes a, not b
print("\(a.name), \(b.name)") // prints "42, -1"
print(NSString(format: "%p", address(o: &a))) // -> 0x109299fc0
print(NSString(format: "%p", address(o: &b))) // -> 0x109299fc8

As you can see, and test yourself in the Playground, when we copy a struct we don’t change the original one. This way we literally copy the values to other variables and the memory address is different too as soon it’s copied.

Copy-on-write in Swift first example

Let’s move to the reference types now.

 

Reference Type Example

If Car were a class the result would be very different, let’s see:

func addressHeap<T: AnyObject>(o: T) -> Int {
    return unsafeBitCast(o, to: Int.self)
}

class Car { var name: Int = -1 }
var a = Car()
var b = a // a is copied to b
a.name = 20 // Changes a and b
print("\(a.name), \(b.name)") // prints "20, 20"

print(NSString(format: "%p", addressHeap(o: a))) // -> 0x600000efa2e0
print(NSString(format: "%p", addressHeap(o: b))) // -> 0x600000efa2e0

As you can see the memory address are the same, and the values of the two variables are the same because they share the same reference in memory. Notice that even the function to get the address in memory is different for reference types because they live in the Heap memory and the value types live in the stack.

The output you can check below:

Copy-on-write tutorial in Swift second example

Now that we’re already comfortable with value and reference types we can go further in our study.

 

Copy-on-write in Swift

As the wiki explains to us:

Copy-on-write (COW), sometimes referred to as implicit sharing[1] or shadowing,[2] is a resource-management technique used in computer programming to efficiently implement a “duplicate” or “copy” operation on modifiable resources.[3] If a resource is duplicated but not modified, it is not necessary to create a new resource; the resource can be shared between the copy and the original. Modifications must still create a copy, hence the technique: the copy operation is deferred until the first write. By sharing resources in this way, it is possible to significantly reduce the resource consumption of unmodified copies, while adding a small overhead to resource-modifying operations.

In Swift, Array, Dictionary, and so on are all value types. And they implement that feature mentioned above. We only have a new instance in memory of them when we modify the buffer. This is a critical memory optimization because those types tend to get bigger and bigger since they aggregate data together. Under the hood, all buffers are implemented to get the maximum memory efficiency.

See the example below:

func address(o: UnsafeRawPointer) -> Int {
    return Int(bitPattern: o)
}

struct Car { var name: Int = -1 }
var a = [Car()]
var b = a

print(NSString(format: "%p", address(o: &a))) // -> 0x6000035289b0 are the same
print(NSString(format: "%p", address(o: &b))) // -> 0x6000035289b0 are the same

a.append(Car()) // now modifying the first one, now Swift should generate the new copy

print(NSString(format: "%p", address(o: &a))) // -> 0x600003511c10 copy on write feature
print(NSString(format: "%p", address(o: &b))) // -> 0x6000035289b0 b still the same 

Now we see how Swift implements the copy-on-write. As you can check above when we COPY the array to another variable, it only copies the reference.

But wait… The Array isn’t a value type? Yes, it is!

Thanks to the copy on write feature, only when we WRITE to the array is when Swift copy that new value to a new memory address. This enables a significantly reduce in resource consumption and when we talk about iPhones the resources aka battery/networking/capacity are limited.

Although array is a value type, therefore it should always copy the value, if you don’t modify the data it won’t copy and this is the beauty of this, you can copy and share arrays, and dictionary instances around without having to think about memory performance.

 

Summary

If you don’t mind let me know in the event that you have got any considerations or criticism, that’s very important to me. This subject isn’t simple but understanding it is pivotal for your advancement as an iOS designer. Copy-on-write is one of the foremost imperative dialect highlights of Swift and ought not to be underestimated.

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or leave a comment saying hello. You can also sponsor posts and I’m open to freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Thanks for the reading and… That’s all folks.

Credits – image