UIKit, many Apple frameworks and observable models are classes, and reference semantics cause classic bugs.
A class looks like a struct but is a reference type: assigning or passing it copies a reference, so all variables point to the same instance. Changing it through one variable is visible through every other. Compare identity with ===.
Classes differ from structs in a few ways:
- No memberwise initializer; every stored property must be initialized in an
init(or have a default). - Inheritance:
class Dog: Animal. Override methods and properties withoverride, call the parent withsuper. Mark a class or memberfinalto forbid overriding, which also lets the compiler devirtualize calls. - Methods can change properties without
mutating, even through aletreference —letonly fixes which object the constant points to. - Deinitializers (
deinit) run when the last strong reference goes away. - Memory is managed by automatic reference counting (ARC).
Initialization has rules: a designated initializer must set all of the subclass's own properties before calling super.init, and a convenience initializer must call another initializer of the same class. required init forces subclasses to implement it.
Use a class when an object has a lifecycle or identity that several parts of the program share (a network session, a view model, a cache). Mark it final unless you design it for subclassing.
Example
class Account {
let owner: String
private(set) var balance: Double = 0
init(owner: String) { self.owner = owner }
func deposit(_ amount: Double) { balance += amount }
func describe() -> String { "\(owner): \(balance)" }
}
final class SavingsAccount: Account {
let rate: Double
init(owner: String, rate: Double) {
self.rate = rate // own properties first
super.init(owner: owner)
}
override func describe() -> String { super.describe() + " @\(rate)" }
}
let a = SavingsAccount(owner: "Lea", rate: 0.02)
let alias = a // same instance
alias.deposit(100)
print(a.describe()) // Lea: 100.0 @0.02
print(a === alias) // trueRemember
- Classes are reference types;
===checks identity - Subclass init sets its own properties before
super.init - Mark classes
finalunless designed for inheritance
Watch out for
- Passing a class instance to several places and being surprised that a change shows up everywhere.