Swift: Swift Classes and Structs

Classes and structs are the two core types for building data models in Swift. They share many similarities but differ fundamentally in memory management and how values are passed. This lesson helps you clarify the selection strategy for each.

1. What You'll Learn


2. A Real-World iOS Developer Story

(1) Pain Point: Changing a User Account in One Place Affects Another

Alice chose class to model a user account:

SWIFT
class UserAccount {
    var name: String
    var balance: Double
    init(name: String, balance: Double) {
        self.name = name
        self.balance = balance
    }
}
let account1 = UserAccount(name: "Alice", balance: 1000)
let account2 = account1  // References the same instance
account2.name = "Bob"
print(account1.name)  // "Bob" — account1 was also changed!

Alice just wanted to create a new user but accidentally mutated the original data. The side effects of reference types propagated throughout the codebase without her realizing it.

(2) Solution: Struct as a Value Type

SWIFT
struct UserAccount {
    var name: String
    var balance: Double
}
var account1 = UserAccount(name: "Alice", balance: 1000)
var account2 = account1  // Copies an independent set of data
account2.name = "Bob"
print(account1.name)  // "Alice" — account1 is unaffected

Struct assignment performs a value copy — each instance is independent.

(3) Benefit: Predictable Data Behavior

Dimension class (Reference Type) struct (Value Type)
Assignment behavior Shares the same instance Independent copy
Accidental mutation risk High (aliasing) None
Memory allocation Heap allocation Stack allocation (more efficient)
Thread safety Requires manual synchronization Naturally isolated
Suitable for Shared mutable state Data models, value semantics

3. Defining Classes and Structs

(1) Basic Syntax Comparison

100%
graph TB
    subgraph class
        A["class Person {"]
        B["    var name: String"]
        C["    var age: Int"]
        D["    func greet() { }"]
        E["}"]
    end
    subgraph struct
        F["struct Person {"]
        G["    var name: String"]
        H["    var age: Int"]
        I["    func greet() { }"]
        J["}"]
    end
Feature class struct Notes
Properties Yes Yes Stored and computed properties
Methods Yes Yes Instance and type methods
Initializers Yes Yes class does not auto-generate memberwise init
Inheritance Yes No struct cannot inherit
Deinitializer Yes No struct has no deinit
Reference counting Yes No struct does not need ARC
Type casting Yes No as?/as! only for class

▶ Example: Defining Classes and Structs

SWIFT
// ============================================
// Class and struct definition comparison
// ============================================
// Class definition — must write init manually
class PersonClass {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    func introduce() {
        print("Hi, I'm \(name), \(age) years old")
    }
}
// Struct definition — automatically gets a memberwise initializer
struct PersonStruct {
    var name: String
    var age: Int
    func introduce() {
        print("Hi, I'm \(name), \(age) years old")
    }
}
let p1 = PersonClass(name: "Alice", age: 28)
p1.introduce()
let p2 = PersonStruct(name: "Bob", age: 22)  // Auto-generated init
p2.introduce()

Output:

TEXT 📖 Display only
Hi, I'm Alice, 28 years old
Hi, I'm Bob, 22 years old

4. Value Types vs Reference Types

(1) Core Differences

100%
graph LR
    subgraph "Value Type (struct)"
        A["var a = Point(x: 1, y: 2)"]
        B["var b = a"]
        C["b.x = 5"]
        D["a.x → 1 (independent)"]
        A --> B --> C --> D
    end
    subgraph "Reference Type (class)"
        E["var a = Point(x: 1, y: 2)"]
        F["var b = a"]
        G["b.x = 5"]
        H["a.x → 5 (shared)"]
        E --> F --> G --> H
    end
Dimension Value Type (struct) Reference Type (class)
Assignment semantics Copy contents Copy reference
Equality check == compares values === compares identity
Storage location Stack (faster) Heap (ARC managed)
let constant Properties are immutable Reference is immutable but properties can change
Function parameters Copy by default Share reference by default

▶ Example: Assignment Behavior Comparison

SWIFT
// ============================================
// Value type vs reference type assignment behavior
// ============================================
// Struct — value type
struct Position {
    var x: Int
    var y: Int
}
var posA = Position(x: 10, y: 20)
var posB = posA
posB.x = 99
print("posA: (\(posA.x), \(posA.y))")  // Unchanged
print("posB: (\(posB.x), \(posB.y))")  // Only affects posB
// Class — reference type
class Location {
    var x: Int
    var y: Int
    init(x: Int, y: Int) { self.x = x; self.y = y }
}
var locA = Location(x: 10, y: 20)
var locB = locA
locB.x = 99
print("locA: (\(locA.x), \(locA.y))")  // Also changed!
print("locB: (\(locB.x), \(locB.y))")

Output:

TEXT 📖 Display only
posA: (10, 20)
posB: (99, 20)
locA: (99, 20)
locB: (99, 20)

(2) The Identity Operator ===

Reference types can use === to check whether two variables point to the same instance:

SWIFT
let loc1 = Location(x: 5, y: 10)
let loc2 = loc1
let loc3 = Location(x: 5, y: 10)  // Different instance
print(loc1 === loc2)  // true — same instance
print(loc1 === loc3)  // false — different instances

Tip: === is the identity operator, distinct from == (the equality operator). Value types can only use == (must implement Equatable) and cannot use ===.


5. mutating Methods and Selection Strategy

(1) The mutating Keyword

Struct instance methods cannot modify properties by default. To do so, they must be marked mutating:

Method Type class struct (non-mutating) struct (mutating)
Modify property Allowed Compile error Allowed
Modify self Allowed Not allowed Allowed
Call requirement No restriction Can be called on let constant Must be called on var

▶ Example: mutating Methods

SWIFT
// ============================================
// Struct mutating methods
// ============================================
struct Counter {
    var count = 0
    // Non-mutating — read-only method
    func display() {
        print("Count: \(count)")
    }
    // mutating — modifies property
    mutating func increment() {
        count += 1
    }
    // mutating — replaces the entire instance
    mutating func reset() {
        self = Counter()  // Replace self
    }
}
var counter = Counter()  // Must be var to call mutating methods
counter.increment()
counter.increment()
counter.display()   // Count: 2
counter.reset()
counter.display()   // Count: 0

Output:

TEXT 📖 Display only
Count: 2
Count: 0

(2) Selection Strategy

Scenario Recommended Reason
Data models (Point, Size, User) struct Value semantics, safe and predictable
Need inheritance (UIView subclass, BaseViewController) class Inheritance is class-only
Need shared mutable state class Reference types share automatically
Need ObjC interop class @objc requires class
Lightweight wrapper (wrapping a few values) struct Stack allocation, zero overhead
Need deinit cleanup class struct has no deinit

▶ Example: Struct as the Default Choice for Models

SWIFT
// ============================================
// Why modern Swift prefers struct for models
// ============================================
struct User {
    let id: Int
    var name: String
    var email: String
    var score: Int
    mutating func addScore(_ points: Int) {
        score += points
    }
}
var user = User(id: 1, name: "Alice", email: "alice@example.com", score: 100)
user.addScore(50)
// Copy-and-mutate pattern — safely create changed versions
let admin = User(id: 2, name: "Admin", email: "admin@example.com", score: 0)
// admin.addScore(10)  // Compile error! let constant cannot call mutating methods
var editor = admin
editor.name = "Editor"  // Independent modification, doesn't affect admin

Output:

TEXT 📖 Display only
(No console output — demonstrates value semantics: editor.name = "Editor" does not affect admin.name)

6. Complete Example: Bank Account System

SWIFT
// ============================================
// Complete example: Bank account system
// Features: class for account management, struct for transaction records
// ============================================
import Foundation
// 1. Transaction record — value type struct
struct Transaction {
    let id: Int
    let amount: Double
    let type: String  // "deposit" or "withdraw"
    let date: Date
    func description() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        return "[\(formatter.string(from: date))] \(type): $\(amount)"
    }
}
// 2. Account — reference type class (shared state)
class BankAccount {
    let accountNumber: String
    var balance: Double
    private var transactions: [Transaction] = []
    private var nextId = 1
    init(accountNumber: String, initialBalance: Double = 0) {
        self.accountNumber = accountNumber
        self.balance = initialBalance
    }
    func deposit(amount: Double) {
        guard amount > 0 else { return }
        balance += amount
        let tx = Transaction(id: nextId, amount: amount, type: "deposit", date: Date())
        transactions.append(tx)
        nextId += 1
    }
    func withdraw(amount: Double) -> Bool {
        guard amount > 0 && amount <= balance else { return false }
        balance -= amount
        let tx = Transaction(id: nextId, amount: amount, type: "withdraw", date: Date())
        transactions.append(tx)
        nextId += 1
        return true
    }
    func printStatement() {
        print("=== Account \(accountNumber) ===")
        print("Balance: $\(balance)")
        print("Transactions:")
        for tx in transactions {
            print("  \(tx.description())")
        }
    }
}
// 3. Usage
let account = BankAccount(accountNumber: "ACC-001", initialBalance: 1000)
account.deposit(amount: 500)
account.withdraw(amount: 200)
account.deposit(amount: 100)
account.printStatement()

Output:

TEXT 📖 Display only
=== Account ACC-001 ===
Balance: $1400.0
Transactions:
  [2026-07-30] deposit: $500.0
  [2026-07-30] withdraw: $200.0
  [2026-07-30] deposit: $100.0

❓ FAQ

Q Why does Apple recommend preferring struct?
A Value types are safer and more predictable. No side effects from shared references, no retain cycles, and no reference counting overhead. Swift standard library types like String, Array, and Dictionary are all structs.
Q Does struct have a performance advantage?
A Yes. Structs are allocated on the stack with no reference counting overhead and no heap allocation cost. However, passing large structs between functions may incur higher copy costs compared to passing a reference. Swift's Copy-on-Write mechanism optimizes this.
Q Must I always write init for a class manually?
A For a class, if all properties have default values and no initializer is defined, Swift generates a default no-argument init. But it never auto-generates a memberwise init like struct does.
Q Can struct have lazy properties?
A Yes. lazy var works in structs too, but because lazy properties cannot be accessed on immutable structs, practical usage is limited. lazy is more natural in classes.
Q Can mutating be used in classes?
A No. mutating only applies to value types (struct and enum). Class methods can modify properties by default — no mutating keyword needed.

📖 Summary


📝 Exercises

  1. Basic: Define a Book struct with title, author, pages properties and a summary() method. Create two instances and test value type copy behavior.
  2. Intermediate: Define a TemperatureSensor class with a currentTemp property and a readings: [Double] history array. Implement recordTemp(_:) and average() methods. Create an instance, record 5 temperatures, and print the average.
  3. Challenge: Design a "Team Collaboration System": use a Team class to manage a shared member list and a Member struct for individual profiles. Team provides addMember(_:) and listMembers() methods. Member has a mutating method to update roles. Verify the shared reference behavior of class.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏