Swift Programming: Structs vs Classes
One of the most important concepts in Swift programming is to understand the difference between a structures and class. They are the building blocks of any application as they encapsulate data by defining methods and properties to provide functionality to our code.
What are structs?
Structs are value types. They are declared using the keyword struct
struct ElectricCar {
var batteryRange: Int
var topSpeed: Int
}
What are classes?
Classes are reference types. They are declared using the keyword class
class ElectricCar {
var batteryRange: Int
var topSpeed: Int
}
Not only do they look syntactically similar, there are many other similarities too.
What are the similarities between Structs and Classes?
- Both can have properties to store values.
- Both can have methods to provide functionality.
- Both can have initializers to define the initial state.
- Both are open to extension.
- Both can conform to a protocol.
- Both can provide a subscript syntax to access values.
Having said that, there are some fundamental differences between the two. Before we jump into them, let’s see what are the unique characterstics of each
What differentiates a class from a struct?
- Classes support inheritance.
- Classes support type-checking.
- Classes support deinitialisers which help us free memory assigned.
- Classes allow reference counting which allow them to have more than one instance.
What’s special about a struct?
- Doesn’t require initialisation. However, if required, we can provide a custom initialiser.
- Doesn’t have inheritance. This makes them lightweight and provide better performance.
The fundamental difference between structs and classes are in their types. As we saw earlier,
structs are value types
whereasclasses are reference type
. Let’s see what this means
What does value types mean?
Value types, when assigned or passed to a function, they create a new copy of the value and store it in a new location. A new instance is created and stored in the current memory stack. Modifying the struct doesn't modify the original value.
Example:
struct ElectricCar {
var batteryRange: Int
var topSpeed: Int
}
var longRange = ElectricCar(batteryRange: 314, topSpeed: 145)
var performance = longRange
performance.topSpeed = 164
print("LONG RANGE::: Top Speed: \(longRange.topSpeed) mph Battery Range: \(longRange.batteryRange) mi")
print("PERFORMANCE::: Top Speed: \(performance.topSpeed) mph Battery Range: \(performance.batteryRange) mi")
Output:
LONG RANGE::: Top Speed: 145 mph Battery Range: 314 mi
PERFORMANCE::: Top Speed: 164 mph Battery Range: 314 mi
The topSpeed
of the performance
variable updates but doesn’t overwrite the longRange
variable. Thats because when longRange
was assigned to performance
, swift creates a copy of the value and stores it to a new location. Hence any modification to performance
doesn’t impact the original longRange
value.
What does reference types mean?
Reference types, when assigned or passed to a function, create a new reference to the existing instance. An instance is created in the heap memory and the reference points to this instance.
Example:
class ElectricCar {
var batteryRange: Int
var topSpeed: Int
//Required initialisers.
init(batteryRange: Int, topSpeed: Int) {
self.batteryRange = batteryRange
self.topSpeed = topSpeed
}
}
let longRange = ElectricCar(batteryRange: 314, topSpeed: 145)
let performance = longRange
performance.topSpeed = 164
print("LONG RANGE::: Top Speed: \(longRange.topSpeed) mph Battery Range: \(longRange.batteryRange) mi")
print("PERFORMANCE::: Top Speed: \(performance.topSpeed) mph Battery Range: \(performance.batteryRange) mi")
Output:
LONG RANGE::: Top Speed: 164 mph Battery Range: 314 mi
PERFORMANCE::: Top Speed: 164 mph Battery Range: 314 mi
The topSpeed
of the performance
and longRange
variable point to the same instance of Car
. So when we perform any modification, it modifies the instance. As there is only one instance, the modification is reflected when we try to access via the other reference.
Some other interesting features about Reference type:
- It also increases the reference count of the original value. Hence, its important to deinitialise any unwanted references.
- Reference Types
longRange
andperformance
are declared as aconstant
The compiler allows us to modify their properties because the constants dont actually change. Its the reference instance which changes. - Swift provides us with Identity Operators which allow us to compare two Instance Types. Its represented by
===
How do we choose between structs and classes?
- Apple recommends we ‘Use structs by default’.
- Use structs for data modelling when you have a remote database which store the final state.
- Use structs if you donot want to reflect the change globally. Since, structs are value types, their scope is limited locally. This hides our changes from other areas of the code. Also, it takes away the risk of accidently changing something globally and makes us more confident of our code.
- If you are creating an inheritance pattern from scratch, use structs and protocols to create the pattern. Protocols are powerful enough to allow structs to adopt to any pattern.
Scenarios where we need to use a class are as follows:
- When our code interacts with a Obj-C code.
- When our code requires sharing instances. We can use a class when different part of our code uses a shared instance or keep track of state.
- When our code requires full control over the state such as File Managers, DB Connections, Network Controllers etc
References:
Thanks for reading and do share any feedback.