Swift Classes and Inheritance
A class is similar to a struct but with key differences. Classes are reference types — multiple variables can point to the same instance. Classes also support inheritance, letting one class build on another.
Struct vs Class – The Key Difference
┌──────────────────────────────────────────────────┐
│ Struct (Value Type) │
│ var a = Point(x:1) │
│ var b = a → b gets its OWN copy │
│ Changing b does NOT affect a │
│ │
│ Class (Reference Type) │
│ var a = Dog() │
│ var b = a → b POINTS to same dog │
│ Changing b DOES affect a │
└──────────────────────────────────────────────────┘
Defining a Class
class Animal {
var name: String
var sound: String
init(name: String, sound: String) {
self.name = name
self.sound = sound
}
func speak() {
print("\(name) says \(sound)")
}
}
let dog = Animal(name: "Dog", sound: "Woof")
dog.speak() // Dog says Woof
Unlike structs, classes do not get a free memberwise initializer — you write your own init method.
Reference Type Behaviour
let petA = Animal(name: "Cat", sound: "Meow")
let petB = petA // petB points to the same object
petB.name = "Tiger"
print(petA.name) // Tiger ← petA also changed!
Both petA and petB reference the same object in memory. Changing one changes both.
Inheritance – Building on Existing Classes
┌──────────────────────────────────────────────────┐
│ Inheritance Tree │
│ │
│ Animal (parent / superclass) │
│ name, sound, speak() │
│ │ │
│ ┌───────┴───────┐ │
│ Dog Cat (child / subclass) │
│ fetch() purr() │
│ │
│ Dog and Cat get everything Animal has, │
│ plus their own unique abilities. │
└──────────────────────────────────────────────────┘
class Dog: Animal {
var breed: String
init(name: String, breed: String) {
self.breed = breed
super.init(name: name, sound: "Woof")
}
func fetch() {
print("\(name) fetches the ball!")
}
}
let myDog = Dog(name: "Bruno", breed: "Labrador")
myDog.speak() // Bruno says Woof ← inherited
myDog.fetch() // Bruno fetches the ball!
print(myDog.breed) // Labrador
super.init calls the parent class initializer. The child class must initialize the parent's properties before adding its own.
Overriding Methods
class Cat: Animal {
init(name: String) {
super.init(name: name, sound: "Meow")
}
override func speak() {
print("\(name) purrs softly and says \(sound)")
}
}
let myCat = Cat(name: "Whiskers")
myCat.speak() // Whiskers purrs softly and says Meow
The override keyword replaces the parent's method with a new version. Swift requires it explicitly so overrides are never accidental.
Preventing Override with final
class Vehicle {
final func startEngine() {
print("Engine started.")
}
}
// Any subclass trying to override startEngine() gets an error.
Mark a method or class with final to block further subclassing or overriding. This is useful when a behaviour must never change.
Identity Operator ===
let a = Animal(name: "Lion", sound: "Roar")
let b = a
let c = Animal(name: "Lion", sound: "Roar")
print(a === b) // true — same object in memory
print(a === c) // false — different objects, same values
The === operator checks whether two variables point to the identical object — not just equal values. This only works with classes.
When to Use Class vs Struct
| Choose Struct | Choose Class |
|---|---|
| Simple data models | Objects with shared state |
| No inheritance needed | Inheritance required |
| Copying is desired | Single shared instance needed |
| Default Swift preference | Working with UIKit/AppKit |
