Skip to content
Theory · Classes · lesson 1 of 10

Classes

What you'll learn

What you'll learn:

  • declare a class with init and explain why two names can lead to one instance;
  • understand that let on a class fixes only the reference, not the instance itself;
  • inherit from a class, override methods and check types with is and as?;
  • choose between a struct and a class and explain the choice in an interview.
One sheet obj with two tags a and b tied to it on strings

Imagine a sheet of paper with a heading — a document. Two tags are tied to the sheet on long strings: one reads a, the other b. Pull either tag and you reach the same sheet.

Rewrite a line after following tag b, and when you follow tag a you see the new line. There is one sheet, and two strings lead to it.

Shared online documents work this way: you can send a link to the document to many people, the document stays one, and everyone sees anyone's edit.

In the structs theme, var b = a made a photocopy of the form, and the copy was filled in separately. A class works like a shared document. Let's see what it looks like in code:

SWIFT
class Document {
    var title = "Plan"
}

var a = Document()
var b = a
b.title = "Final"
print(a.title) // Final

Let's go through the program line by line:

  1. class Document declares a class with one property, title. As in a struct, the property has a default value.
  2. var a = Document() creates an instance of the class — that sheet — and gives the name a a tag to it.
  3. var b = a does not create a second sheet. The name b gets a second tag to the same instance.
  4. b.title = "Final" rewrites the heading of the shared sheet.
  5. print(a.title) prints Final: tag a leads to the same sheet, so the edit shows.

If Document were a struct, the program would print Plan: b would be a separate copy.

Why have such a type? Sometimes several parts of a program work with one and the same thing: a bank account, a game character, an open document. If each part gets its own copy, the changes drift apart. A class gives every part of the program one shared instance.

A reference instead of a copy

A class is a type that, like a struct, groups properties and methods, but is not copied on assignment. A class variable holds not the instance itself but a reference to it — that tag on a string. Assignment copies the reference, and the instance stays one. An instance of a class is also called an object.

A class is a reference type, like a closure. Structs, arrays, Int and String, on the other hand, are value types: for them, assignment copies the value itself.

The === operator checks whether two references lead to the same instance. It answers true only when the instance is one and the same, and the !== operator answers the opposite. They work only with classes: a struct has no reference to compare.

Click "Next step" and watch a.title and b.title: they change together because there is one instance. And c appears on its own.

LiveTwo references, one instancestep 1 of 8 · frames: 1

Document() creates an instance, and a gets a reference to it.

Watch a.title and b.title: they change together

Each call to Document() creates a new instance — a new sheet. So c has no link to a and b, and a === c answers false.

Here the sheet picture falls short. A real document can be copied with a button, but a class has no such button. var b = a always gives one more tag, and only a call to Document() creates a new sheet.

Interviewers often give a junior a program with a struct and ask what it prints if struct is replaced with class. The answer: a struct is copied on assignment, while a class instance stays shared, and an edit through one name is visible through the other.

A class and init

A struct gets a memberwise initializer by itself, for example Point(x: 1, y: 2). A class gets no memberwise initializer. If every property has a default value, the class has an empty init(): that is how we created Document().

If at least one property has no default value, you write init yourself:

SWIFT
class Account {
    var owner: String
    var balance = 0
    init(owner: String) {
        self.owner = owner
    }
}

let anna = Account(owner: "Anna")
print(anna.owner, anna.balance) // Anna 0

Let's see how the account is created:

  1. owner has no default value, so init must set it.
  2. init(owner: String) takes the owner as a parameter, and self.owner = owner puts it into the property of the new instance.
  3. balance is already 0, and init leaves it alone.
  4. Account(owner: "Anna") calls this init, and print prints Anna 0.

Common mistake

A class is declared like a struct: with a property that has no default value and no init.

SWIFT
class Profile {
    var name: String // ✗
}
TEXT
error: class 'Profile' has no initializers

The program does not compile: a class has no memberwise initializer, so nothing can set name. Below the error, the compiler also names the cause — the property name without an initial value. There are two ways to fix it:

  • give the property a default value: var name = "";
  • write init(name: String), as in Account.

Key points

  • A class is declared with class; a class variable holds a reference to an instance, not the instance itself.
  • var b = a copies the reference: a and b lead to one instance, and an edit through one name is visible through the other.
  • === checks whether it is the same instance; each call such as Document() creates a new one.
  • A class gets no memberwise initializer: if a property has no default value, the class needs its own init.