Skip to content
AgileLogics
6 min read

Classes and Reference Types

Prerequisites: Initializers

A class looks a lot like a struct, with one fundamental difference: classes are reference types. A variable holding a class instance holds a reference to a single shared object, not its own copy.

Syntax

class BankAccount {
    let owner: String
    private(set) var balance: Double

    init(owner: String, balance: Double = 0) {
        self.owner = owner
        self.balance = balance
    }

    func deposit(_ amount: Double) {
        balance += amount
    }
}

Differences from structs you can see here:

  • Classes have no automatic memberwise initializer — you write init yourself.
  • Methods that change properties don't need mutating.

Shared references

class BankAccount {
    var balance: Double = 0
}

let mine = BankAccount()
let alias = mine          // not a copy — the same account
alias.balance = 100

print(mine.balance)
print(mine === alias)
100.0
true

Changing alias changed mine, because both refer to one object. === checks whether two references point to the same instance.

Also notice that mine is a let, yet its balance changed. For a class, let means the variable always refers to the same object — not that the object's contents are frozen.

deinit

A class can run cleanup code when its instance is destroyed:

class Connection {
    init() { print("Connected") }
    deinit { print("Disconnected") }
}

do {
    let connection = Connection()
    print("Using \(type(of: connection))")
}
print("Done")
Connected
Using Connection
Disconnected
Done

When nothing refers to an instance any more — here, when connection goes out of scope at the end of the do block — Swift frees it immediately, running deinit. Level 3 explains how this automatic reference counting works.

Common mistakes

  • Passing a class instance into a function and being surprised that the function's changes are visible outside.
  • Using a class for simple data where a struct would do — see the next lesson.