Struct vs Class: Which One Do I Need?
It's an age-old question that is asked frequently in iOS interviews, what is the difference between a class and a struct?
In this article I'll go over the two and how they work in memory so you can gain complete knowledge about the topic.
Before starting, it's important to note that structs or classes are the flexible constructs that become the building blocks of your program's code.
Value Types
Even before I start explaining what is the difference between classes and structs we should start with the basic definitions. In Swift, we have value types and reference types.
What does that mean exactly?
A value type is a type whose value is copied when it's assigned to a variable or a constant, or when it's passed to a function [1].
Let's see with an example.
Imagine that we're creating a struct called Rabbit. This has all of the properties of a rabbit like the nose, tail, ears you name it.
If I create a variable of that type called element1 and create another variable called element2 that is equal to element1, what will happen?
In this case, since the type is a value type, it will create an exact copy of the previous element.
But now what happens when you're trying to change element2? Will element1 be changed?
No, since Rabbit is a value type when you create element2, all values are copied when passed around.
Since value types are passed around through copies, this means that when referring to another variable it doesn't incur the overhead of reference counting [1].
And returning to the initial question. Structs and enums are value types.
Fun fact: All basic types in Swift like integers, floating-point numbers, booleans, strings, arrays and dictionaries are value types [2].
Reference Types
According to Apple's official documentation:
Unlike value types, reference types are not copied when they are assigned to a variable or constant or when they are passed to a function. Rather than a copy a reference to the same existing instance is used [3].
Let's also create an example to see what is a reference type.
Let's imagine that I created a class called Cat. This class has all of the elements that identify this animal.
Now I create an instance of type bear called element1 and create another instance called element2 that references element1.
If I change something on element2 will it also change element1?
Since both instances refer to the same instance they are just two different names for the same single instance.
This may cause some difficulties if we escalate this into a complex system. Every time I use element1 in my code I also need to worry how it will affect element2 and vice-versa.
So in this case both instances are pointing to the same address in memory.
They don't contain the element itself, but a reference to it and this allows other instances to contain the same reference. This instance can be mutated through any of its referencing variables [4].
Classes are reference types.
Similarities Between Structs and Classes
Here are the similarities between structs and classes:
- Use properties to store values
- Define methods
- Define initializers
- Allow the extension to expand its functionality
- Can conform to protocols
You can learn more common points of both constructs through Apple's official documentation here [5].
Differences Between Structs and Classes
Here are the main differences between structs and classes:
- You need to explicitly create the init for a class bu
- Classes can inherit from other classes but this doesn't happen in structs
- You can typecast to check and interpret the type of a class (using is or as operators)
- You can deinitialize an instance of a class using deinit(), freeing up resources
- Reference counting allows more than one reference to an instance of a class.
You can learn more differences through Apple's official documentation here [5].
So what should I choose?
Choose Structures by Default
Both types are great choices for storing data, however, according to Apple's official documentation, you should choose structures by default.
When choosing structs, you don't need to consider the rest of your app. What this means is that local changes to a structure aren't visible to the rest of your app unless you intentionally communicate those changes [6].
If you change an instance of a structure, those changes are localized to that instance and do not affect other parts of your application.
Choose a Class When You Need to Control Identity
Classes in Swift have the built-in notion of identity because they are reference types.
This means that if you have two different class instances that have the same values for each property they are still considered different [7].
Use Structures and Protocols to Model Inheritance
Structs cannot support inheritance from other classes, however, they can adopt protocols. According to Apple, if you're building an inheritance relationship from scratch you should try to build the hierarchy using protocol inheritance [8].
✨Bonus: Memory Management in Value and Reference Types
If you want to deepen your knowledge on how value and reference types work in Swift it's important to talk about memory management.
Before going through how this works we should talk about the different types of storage locations. The main types are stacks and heaps.
Stack Memory
In every computer's CPU there is a stack, this is nothing more than a list with LIFO (Last-In First Out). It's a storage device that stores information in such a manner that the item stored last is the first item retrieved [9].
A Stack Memory employs an automatic allocation and deallocation of memory that stores temporary data [10].
Heap Memory
Compared to a stack memory, a heap memory operates dynamically, this means that the program can allocate or deallocate memory areas of different sizes when necessary [10].
The size of the heap is not fixed and can be dynamically adjusted at runtime. So we can say that heap memory is more suitable for dealing with large, complex data structures [10].
Memory Management in Swift for Structs and Classes
It's also very common to see online that structs are usually stored in the stack and classes are usually stored in the heap.
However, it's important to note that in Apple's official documentation and Swift's Programming Language book, there is no mention of a straightforward answer to this.
And since this is not a new concept to begin with we can do more research on this in other languages like C#. As Eric Lippert, creator of C# put it:
The choice of allocation mechanism has to do only with the known required lifetime of the storage [11].
He goes even one step further:
It is usually stated incorrectly: the statement should be “value types can be stored on the stack”, instead of the more common “value types are always stored on the stack”.
Some concepts of this language are transversal to Swift:
- Every storage location has a lifetime in which the storage contents are valid [11]
- Long-lived storage locations are always heap locations [11]
- Short-lived storage locations are always stack locations [11]
We can say that the type system is not related in any way to the storage allocation strategy [11]. I highly recommend you check the full article on Eric Lippert's blog.
Conclusion
Both Structs and Classes are tools in our toolbox. Structs are less capable than classes but in return, they are simpler. You don't need to worry about references, lifecycle, subtypes or reference cycles for example [12].
Also, structs offer better performance, especially for small elements. If you think about the primary types like Bool in Swift, if they were classes they would take a lot more memory to store references to actual instances [12].
It depends on the case you have at hand.
Thank you for taking the time to read this article.
✨ I've created a simple question page that you can use to test your knowledge on this topic. You can download it here.
If you've enjoyed this you can follow my GitHub profile and connect with me through LinkedIn or X.
References
[1] Copy-On-Write Tradeoff: Advanced Swift by Chris Eidhof, Ole Begemann, Florian Kugler and Ben Cohen page 184
[2] The Swift Programming Language Book: 5.7 Edition page 166
[3] Classes Are Reference Types: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures/#Classes-Are-Reference-Types
[4] Value Types and Reference Types: Advanced Swift by Chris Eidhof, Ole Begemann, Florian Kugler and Ben Cohen page 163
[5] Comparing Structures and Classes: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures/#Comparing-Structures-and-Classes
[6]Choose Structures by Default: https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes#Choose-Structures-by-Default
[7] Use Classes When You Need to Control Identity: https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes#Use-Classes-When-You-Need-to-Control-Identity
[8] Use Structures and Protocols to Model Inheritance and Share Behavior: https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes#Use-Structures-and-Protocols-to-Model-Inheritance-and-Share-Behavior
[9] Stack Organization: Computer System Architecture M.Morris page 249
[10] Memory Stack vs Heap: https://www.baeldung.com/cs/memory-stack-vs-heap
[11]The Truth About Value Types by Eric Lippert https://ericlippert.com/2010/09/30/the-truth-about-value-types/
[12] Deciding Between Structs And Classes: Advanced Swift by Chris Eidhof, Ole Begemann, Florian Kugler and Ben Cohen page 179