iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Classes

Swift classes: reference types, inheritance, identity, deinit, and the rules that distinguish class from struct.

Swift — classes

EXAMPLE
// ===== Class basics =====
class User {
    var name: String
    var email: String
    init(name: String, email: String) {
        self.name = name
        self.email = email
    }
}

let u = User(name: "Alex", email: "a@x.io")
u.name = "Sam"   // mutation through let — let only locks the REFERENCE, not the object

// ===== Reference vs value semantics =====
let u2 = u
u2.name = "Bob"
print(u.name)    // 'Bob' — both refer to the same object

// (Struct would have copied; class shares.)

// ===== Identity =====
let u3 = User(name: "Bob", email: "a@x.io")
u === u2    // true (same reference)
u === u3    // false

// ===== Inheritance (open + final + override) =====
open class Vehicle {
    open var description: String { "vehicle" }
    open func describe() { print(description) }
}

class Car: Vehicle {
    let model: String
    init(model: String) { self.model = model }
    override var description: String { "car: \(model)" }
}

final class SportsCar: Car { }   // cannot be subclassed

// Default classes are NOT open; you must mark 'open' to enable inheritance across modules.

// ===== Initialisers =====
class Animal {
    let name: String
    init(name: String) { self.name = name }
    convenience init() {
        self.init(name: "Unknown")     // delegates to designated init
    }
}

// Required initialisers must be implemented by subclasses:
class Base {
    required init() {}
}
class Sub: Base {
    required init() { super.init() }
}

// ===== Deinit =====
class Logger {
    init() { print("opening log") }
    deinit { print("closing log") }
}

// Runs when the last strong reference is released.

// ===== Reference cycles =====
class Parent {
    var child: Child?
}
class Child {
    weak var parent: Parent?     // weak avoids retain cycle
}

// Use 'weak' for back-references; 'unowned' when the lifetime is guaranteed equal.

// ===== Closures + self =====
class ViewController {
    var name = "Alex"
    func setup() {
        someAsync.then { [weak self] in
            guard let self else { return }
            print(self.name)
        }
    }
}

// ===== Static + class members =====
class Counter {
    static var shared = Counter()        // type-level (cannot be overridden)
    class var label: String { "counter" } // CAN be overridden by subclass
}

// ===== Final = optimisation + intent =====
// 'final' on classes / methods / properties tells the compiler + reader:
// 'this cannot be subclassed / overridden'. The compiler may devirtualise calls.

// ===== When to use class vs struct =====
// CLASS:
//   - Identity matters (database entity, persistent object)
//   - Shared mutable state
//   - Interop with Objective-C / Cocoa frameworks
//   - Deep inheritance hierarchies (rare; prefer composition)
// STRUCT:
//   - Value types (Money, Point, Vector)
//   - Small / immutable
//   - Most modern Swift code

// ===== Patterns to internalise =====
// - Default to struct; reach for class only when you need identity / inheritance
// - weak / unowned for back-references to avoid cycles
// - [weak self] in closures
// - final by default; open only when designed for inheritance

// ===== Pitfalls =====
// - Inheriting deeply when composition fits better
// - Forgetting [weak self] in long-lived closures -> retain cycles
// - Sharing a class instance and mutating from multiple owners -> race / surprise
// - Subclassing for reuse instead of behaviour relationship

Why it matters

Classes are Swift reference types: identity, inheritance, deinit. Reach for them when you need shared mutable state or framework interop; default to struct otherwise. The discipline (weak back-refs, final by default, composition over inheritance) keeps hierarchies shallow and predictable.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
class Counter {
    var n = 0
    func bump() { n += 1 }
}
let c = Counter()
c.bump()
Try it Yourself »

Discussion

Loading…