Ghostboard pixel

Swift: The differences between structs and classes

Swift: The differences between structs and classes

Compared to many other programming languages, structs are very powerful in Swift. Hence, they should be used more often.

Hint: This post is using Swift 5

Similarities

First, let’s start by talking about the similarities between structs and classes. They actually have a lot in common:

  • they have methods and initializers
  • they have stored and computed properties
  • they can be extended 
  • they can implement protocols 

There are some differences how you use these features, but those are minor and are not the main part of this post. For example, initializers for structs are created automatically, whereas for classes you have to do that manually.

Differences

There are three main differences between structs and classes:

  • Structs are value types, whereas classes are reference types. 
  • Classes can be inherited. This is not possible for structs. 
  • Objective-C interoperability for structs is very bad, whereas it’s very good for classes.

Let’s look at these differences in detail.

Value types vs reference types

So what is the difference between a value type and a reference type? An instance of a reference type cannot be accessed directly, but only by using a so-called reference. Therefore, multiply references can point to the same instance. Take a look at the following example:

class TestClassA {
    
    var a: Int
    
    init(a: Int) {
        self.a = a
    }
    
}

let testA = TestClassA(a: 5)
let testB = testA

print(testA.a) //5
print(testB.a) //5

testA.a = 10

print(testA.a) //10
print(testB.a) //10

As you can see, just one instance of TestClassA has been created, but two references are pointing to it. If you change the property a of testA, the same happens for testB because it’s the same instance.

Now let us look at a struct:

struct TestStruct {
    var a: Int
}

var testC = TestStruct(a: 5)
var testD = testC

print(testC.a) //5
print(testD.a) //5

testC.a = 10

print(testC.a) //10
print(testD.a) //5

Again, we have two values (testC and testD). If a property of one value gets changed, this doesn’t happen for the other one because it’s not the same instance.

Note also that testC and testD has to be declared as variable by using the keyword var. It is not possible to change a property of a struct, if the struct itself is a constant.

It’s exactly the same as with some Swift types, like Int, String and Array – they are also value types. But not only that, they are also structs!

The corresponding example to the one above with Int values:

var testE = 5
var testF = testE

print(testE) //5
print(testF) //5

testE = 10

print(testE) //10
print(testF) //5

If you haven’t worked with structs yet, the last example is probably much more natural for you. Just remind yourself that a struct works just as an Int value because an Int is a struct.

Reference types and value types behave quite differently. Imagine you have an instance of a reference type and you have multiple references pointing to it within your app. If you change the value by using one of these references, all of these references point to the same new value. This can be a wanted behavior. But if that’s not the case, this can be the source of major bugs. 

Because of the differences between value and reference types, memory management is quite different as well. The most important point is that so-called retain cycles can occur for reference types. That is not possible for value types. Take a look at this post for more details about this topic.

Objective-C interoperability 

Structs in Swift don’t have a good Objective-C interoperability. If you define a struct in Swift, you can’t expose it to Objective-C source code. That means for example that a delegate or datasource of UITableView must not be a struct but a class because UIKit is still written in Objective-C. This sounds like a major obstacle, but in reality these cases are rarer than you might think. 

Inheritance 

To make it short: There is no inheritance for structs. However, there are other ways to implement similar behaviour for structs. The most common way is using protocols. 

Use structs by default 

So the general guideline is that you should structs by default. The reason is that they are easier and safer to use: Retain cycles cannot occur and you cannot change the value for another part of the app by accident. 

Only if you need class specific features like inheritance, Objective-C interoperability, or you want the specific behaviour of a reference type, you should use classes. 

References 

Title image: @  Stepan Kapl / Shutterstock.com