Structures vs Classes in Swift

      6 Comments on Structures vs Classes in Swift

You might have heard that some of the swift native types are being converted from classes to structures. In this article, we'll explore structures and classes, we'll talk about values, references, stacks and heaps. Hopefully, by the end of it, you'll understand the differences between them.You might have heard that some of the swift native types are being converted from classes to structures. In this article, we’ll explore structures and classes, we’ll talk about values, references, stacks and heaps. Hopefully, by the end of it, you’ll understand the differences between them.

Reference Types and Value Types

If you’re coming from Objective-C, or any other modern language, you should be fairly familiar with reference types (fancy name for classes, really). Value types are all around us… int, double, bool… They’re all value types, we call them primitive types. We can build our own value types, we call them structs. You’ve probably seen some value types before, CGRect is a structure (value type), for example. Before jumping in the code, and explaining the differences between the two, let’s make a little interlude…

The Heap and The Stack

Every time you create a class, it’s created on something we call ‘a heap’. You can visualise it as a big pile of objects. You create a class, throw it on the heap, and you need a way of finding it. You find the object by keeping a reference to it, a memory address. Think of it as a string connecting your object and your variable. You tug on your string, and you can find your object on the heap and use it. You can create a new variable and assign it a reference to the existing object. Your new variable will contain a copy of the reference to the object. In the example below, ‘earth’ and ‘backupEarth’ are both dealing with the same object. You’ll see some code later.

One analogy that I can think of when thinking about a heap is a football stadium. Imagine you’re standing in the middle of the stadium, on the field. The stadium is full of people, and among those thousands of people is your friend. If you have your friend’s phone number, you can always reach him. If someone else is on the stadium next to you, they can copy your friend’s phone number and reach him as well. If your battery dies, the guy who copied the number off you can still reach your friend.

That’s the heap. The stack is a bit different.

When your app executes, it creates objects and variables. Classes are initialised, functions run, things can get pretty wild. We know the classes are created on a heap, so where is everything else? Variables, primitives and structs? They all live on the stack. Stack is literally a stack of value types. When you create a class you get a stack frame, you populate it with your variables. When you call a function you get another stack frame, that sits on top of your previous frame. When you call a for loop inside your function, you get another… Every time a scope is changed, a frame is pushed (or popped) on the stack.

This all sounds a bit confusing, here are some pretty diagrams:

This is a stack with three frames on it, here’s the code that created this stack:

// Frame 1
let earth = Planet(name: "earth", galaxy: "current one")

let backupEarth = earth
backupEarth.name = "backup earth"

func populateEarth() { // Frame 2
    let increment = 12
    for i in 0...25 { // Frame 3
        earth.population += increment * i
    }
}

print(earth)

populateEarth() // Push frame 2

print(earth)

When we created a class, we pushed ‘frame1’ on the stack with all the instance variables that the class has (the class itself is on the heap). When we called the function ‘populateEarth’, we pushed ‘frame2’ on the stack. Within that function is a for loop, that’s another frame. When the loop finishes, ‘frame3’ will be popped off the stack and all its variables will be destroyed. Same goes for all other frames.

As you can imagine, the stack is pretty dynamic. Frames get pushed and popped all the time. Pushing a frame takes some memory (since each frame has its own segment of memory). If you push too many frames on your stack you’ll get the famous stack overflow.

Pushing frames on a stack is a lot like writing a novel. Imagine a writer writing his latest masterpiece. He starts by writing a page, he writes a dozen more, he realises his last two pages were crap, he throws them in the bin, he writes some more, the throws some more pages in the bin. The writer can look at his pile of pages, and he can always read back and have access to all the characters, places and events, but he has no idea what’s ahead.

Let’s finish this little detour with a note… Reference types (classes) live on the heap, value types (structures) live on the stack.

Classes

As seen on the diagram above, when we create a class on the heap, we can have multiple references to the same class. This means that we can change the state of the class from multiple places in our app. Let’s go through a quick code example:

class Planet: CustomDebugStringConvertible {
    var name: String
    var galaxy: String
    
    init(name: String, galaxy: String) {
        self.name = name
        self.galaxy = galaxy
    }
    
    var debugDescription: String {
        return "\(name):\(galaxy)"
    }
}

let earth = Planet(name: "earth", galaxy: "current one")

let backupEarth = earth
backupEarth.name = "backup earth"

print(earth)

We have a simple class called ‘Planet’. We instantiate the class, and assign it to a variable ‘earth’. This variable holds a reference to our instance of the ‘Planet’ class on the heap. When we create a new instance variable ‘backupEarth’ and assign the existing earth to the backupEarth, the reference is copied to the new variable. Now he have two identical references pointing to the same object. Remember from the heap example above, we have two people having a phone number of a guy sitting somewhere in a football stadium.

To prove that ‘earth’ and ‘backupEarth’ are in fact pointing to the same object, we change a property on the ‘backupEarth’. If we print the ‘earth’ now, we’ll see that the name has been changed from ‘earth’ to ‘backup earth’.

Swift copies variables on assignment. For example, in this line:

let backupEarth = earth

Swift copied the value of the old variable (which is a reference to an object) into a new variable. This is subtle, but really important to understand, you’ll understand why when we start talking about structures.

Built-in Reference Types

In swift, some built-in types changed from reference types to value types. Array is one example. We’ll cover the new array later, let’s see how the old array works.

var planets: NSMutableArray = [Planet(name: "abc123", galaxy: "andromeda"), Planet(name: "xyz987", galaxy: "milkyway")]

var newPlanets = planets

newPlanets.add(Planet(name: "earth", galaxy: "this one"))

print("Original: ", planets)
print("Copy: ", newPlanets)

This is the good old array from Objective-C that we’re all used to. Taking into account what we learned so far, NSMutableArray is a class, we save a reference to it in the ‘planets’ variable that gets copied to ‘newPlanets’ variable. When we add a new planet and print out both references, we can see that they both print out the same thing.

Structures

We learned so far that structures are value types and that they live on the stack. When you create a structure and assign it to a variable, the structure itself is assigned to that variable. When you create a new variable and assign the old variable to the new one, the structure is copied. Now you have two of them. Let’s see this in code:

struct Book {
    var title: String
    var author: String
}

let originalBook = Book(title: "How to survive on less than 2000€ in Dublin", author: "Anonymous")
var copiedBook = originalBook
copiedBook.author = "Some guy"

print(originalBook)

We create a structure ‘Book’ and assign it to the variable ‘originalBook’. Then we create a new variable ‘copiedBook’ and assign the original book to it. We said before that swift copies variables on assignment. The value of our variable ‘originalBook’ is the structure itself, it will be copied to ‘copiedBook’. To prove that we have two structures in memory, we change the author property of the copied book. If we print the ‘originalBook’ now, we can see that its author didn’t change.

Built-in Value Types

Some built-in types have changed from reference types to value types, most notably, arrays and dictionaries. Arrays are a workhorse of any application, so we’ll demonstrate this change on a short code snippet:

var planets = [Planet(name: "abc123", galaxy: "andromeda"), Planet(name: "xyz987", galaxy: "milkyway")]

var newPlanets = planets

newPlanets.append(Planet(name: "earth", galaxy: "this one"))

print("Original: ", planets)
print("Copy: ", newPlanets)

This example is almost identical to the reference type example we used above. The only difference is that we’re using the new swift array. As in the example above, we create an array of planets, we assign it to a variable. Then, we create a new variable, and assign the planets array to the new variable. Since array is a structure, it got copied to our new variable. To demonstrate this, we add a new planet to ‘newPlanets’ array and print both arrays to the console. You can see that the new planet only got added to the ‘newPlanets’ array.

These two examples demonstrate the core difference between value types and reference types.

What to Use

You must be wondering, which of the two should I use in my applications? Short answer is, use structures, unless you have a really good reason not to.

More and more of swift native types are being converted to structures, there’s a good reason why the guys at Apple decided to do this. One of the most difficult bugs you’ll ever encounter are threading bugs. Where you have multiple threads accessing the same objects and modifying them. We had a way of dealing with those by synchronising access to objects, copying collections before iterating over them… But no one is perfect, and the bugs crept in.

With structures (and value types in general) things are quite different. They are inherently thread safe, because they get copied around and they live on the stack. Every thread that gets created gets its own stack. Once you start working with a structure in your class/method, you can be certain that no one will pull the rug under you and that that structure will remain under your full control until you’re finished working with it.

Sometimes you’ll want to work on a reference type, and you’ll want all the threads to share the same data. That’s fine, as long as you’re careful when you’re changing the shared objects you’ll be fine.

Conclusion

Just like memory management, I believe it’s essential for developers to understand the difference between value types and reference types. Especially now, when swift is converting so many of its own types to structures. If you treat your value types as you were treating your reference types in Objective-C (arrays, being one example), you might create some really weird, hard to find, bugs for yourself.

Structures are here to stay, and they are here to make your life easier. We all have nightmares debugging threading issues and, personally, I’ll take any help I can get with those.

I hope I cleared the fog for you with this article and that you learned something new today. If I muddied the waters for you, let me know and I’ll try better next time. You can find a small playground on my GitHub profile.

As always, have a nice day 🙂

Dejan.

Edit

  • Some guys on reddit made a fair point that some structures actually live on the heap, but I didn’t want to confuse you with that implementation detail, you can read a great article on the subject here. I would suggest that you treat structures like any other value type in your code. (5th of September 2017)

More resources

6 thoughts on “Structures vs Classes in Swift

  1. Paweł Dropski

    You wrote “Every time you create a class, it’s created on something we call ‘a heap’.” … When we created a class, we pushed ‘frame1’ on the stack. and then ” and you sum up “Reference types (classes) live on the heap, value types (structures) live on the stack.” Is this correct?

    Reply
    1. Dejan Agostini Post author

      Yes, variables live on the stack. But you’re right, it sounds a bit confusing. I’ll change it right away. Thanks for noticing!

      Reply
  2. Sam

    This isn’t right at all. Most Swift structs are actually heap allocated. Have you tried looking at the memory allocations of a program?

    Reply
    1. Dejan Agostini Post author

      You’re right. But that’s an implementation detail. If I went into such detail I believe it could have been confusing. If you think of all structs as living on the stack, you’d be OK. What do you think?

      Reply
  3. Jaminyah

    Regarding “use structures, unless you have a really good reason not to” it may be helpful to point out that if your code is such that there are properties that need to be observed then a struct cannot be used. This is because Key Value Observing requires that the entity inherits from NSObject.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.