Introduction To Swift Memory Layout

In the previous article, we discussed some features of Swifts Value Type semantics. Today, we are going to take a look at the fundamentals of how different Swift types are allocated and what that means for our apps performance.

Reference Type Objects

A simple class is a Reference Type, just like mentioned in the last article. This comes with a few features that most of us learn about pretty early. For example, a single class object can be referenced and manipulated by several variables. But what happens when we create an instance of a class?

class MyClass {
var a: Int
init(a: Int) {
self.a = a
}
}
var A = MyClass(a: 1)

The example above provides a simple class that allows us to start looking into what happens when we create a variable A that references a class type.

When we call the MyClass initializer, a call is made to allocate enough memory to store our object in memory, and this is where we need to take a look at the first of two very important parts of our main memory, namely The Heap.

The Heap is a part of our memory that is used for dynamic allocation. What this means is that if we need to create a new Reference Type object on the fly when running our application, we need to ask for space on The Heap. Since this part of our memory need to be able to serve multiple threads at once without corrupting any data, it also needs mechanisms to avoid allocating the same memory block twice and to deal with parallel allocation calls. Without going into the details of how this is managed, let’s continue with the assumption that this actually takes a fair bit of time.

Once a sufficient block of memory has been found and allocated for us, our application can finish initializing our object. Apart from initializing the actual values in our class, there are also some metadata values being initialized (this includes type data and reference counters). Swift uses something called Automatic Reference Counting (ARC) to keep track of which objects are actually being used. If an object is not referenced by any variables, it can be safely removed and the memory block can be returned to be used for something else. This is a great function, but it comes with the overhead of updating our counters every time an object is created or shared.

Normally, the time this takes isn’t something you would notice if you’re just allocating one new object. However, if your program is constantly creating new objects like this, the costs will add up and you may end up wasting resources that could have been better used doing something else.

Value Type Objects

As I mentioned in the previous article, struct and enum (among others) are both Value Types. The question we need to ask ourselves is: where will a Value Type be allocated? This part will get a little bit tricky. Let’s start by looking at The Stack — the second of the two important main memory parts.

The Stack is a part of memory that gets its name from the way it operates — like a stack. The idea is that you have a stack of data and if you add any new data, it’s added to the top. Removing data is also restricted to the top end of our memory space, so we can only remove the data that is currently placed at the top. The system is using a simple pointer to keep track of where the top of our stack memory is currently located. This means that allocating and deallocating memory is done by a simple increment or decrement operation, and is therefore super fast. On the downside, this structure is not very compatible with the kind of dynamic allocation that The Heap can offer.

Conceptual sketch of allocation / deallocation on the stack.

Going back to our Value Types and the question of where they end up, the answer is anti-climactic, to say the least: “It depends”.

struct Point {
var x: Double
var y: Double
}
var P = Point(x: 1, y: 2)

Let’s say we have the above struct to represent points in a 2D coordinate system. Being a Value Type of a fixed and relatively small size (as well as only consisting of other Value Types), this struct will end up on The Stack, meaning that allocation is done by decrementing the stack pointer two steps and putting our x and y values there. The pointer is decremented because The Stack, by convention, grows “downwards”.

What about the Array type? A Swift array is declared to be a struct, which means it should reside on The Stack as well. Actually no, this is where different Value Types start to differ. An array is a dynamic type, meaning it can grow and shrink in size during runtime. It is not always the case that we know exactly how many values will be in an array, so it needs to be able to resize itself to fit your needs. Therefore, an array will be treated more like a Reference Type in this aspect. It will end up on The Heap, which means it has to go through the same allocation process as our class did (there are exceptions, but those are mainly made up of pretty useless edge cases). This makes sense for this particular data structure, and the cost of allocating an array is made up for by the fact that it will be way easier to resize it later. If you know exactly how many values an array will hold, you can further reduce the cost of allocating it by using the .reserveCapacity(_:) method. This will allocate the correct memory size from the get-go, and you won’t incur any more allocation costs.

How To Choose?

The case for using Reference Types typically involves situations when you need to be able to share mutable states between different parts of the program and want to avoid allocation of new memory. The need for a Reference Type is also pretty self explanatory if you need to subclass some part of an external framework (as is often the case with UIKit).

Pretty much every other situation will lend itself to using Value Types. Swifts Value Type semantics will often improve performance thanks to the way they are allocated and managed, and they will almost always make it easier to track down bugs.

That’s it for this week! If you want to learn more about how different types will impact your apps performance, you can check out this presentation where Mike Ash goes into some serious detail about how Swift handles memory layout.

Feel free to comment if you have questions, and follow to get notifications about future articles.

Data Scientist | Software Engineer | Author

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store