Bird
Raised Fist0
Swiftprogramming~15 mins

Comparing structs vs classes decision in Swift - Trade-offs & Expert Analysis

Choose your learning style10 modes available
Overview - Comparing structs vs classes decision
What is it?
In Swift, structs and classes are two ways to create custom data types. Structs are value types, meaning each instance keeps its own copy of data. Classes are reference types, so instances share a single copy and changes affect all references. Choosing between them affects how your data behaves and how your program runs.
Why it matters
Choosing structs or classes changes how your data is stored and shared. Without understanding this, your program might have unexpected bugs or performance issues. For example, using classes when you want independent copies can cause confusing side effects. Knowing when to use each helps you write safer, clearer, and faster code.
Where it fits
Before this, you should understand basic Swift syntax and how to create simple types. After this, you can learn about memory management, protocols, and advanced Swift features like value semantics and reference counting.
Mental Model
Core Idea
Structs copy data when assigned or passed around, while classes share a single instance through references.
Think of it like...
Think of structs like photocopies of a document—each copy is separate and changes don’t affect others. Classes are like sharing a single document with friends; if one person edits it, everyone sees the change.
┌───────────────┐       copy       ┌───────────────┐
│   Struct A    │ ───────────────▶ │   Struct B    │
│ data: 10     │                 │ data: 10     │
└───────────────┘                 └───────────────┘

┌───────────────┐       ref        ┌───────────────┐
│   Class A     │ ───────────────▶ │   Class B     │
│ data: 10     │                 │ data: 10     │
└───────────────┘                 └───────────────┘

Changing Struct B does NOT affect Struct A.
Changing Class B changes Class A too.
Build-Up - 7 Steps
1
FoundationUnderstanding value types with structs
🤔
Concept: Structs are value types that copy their data when assigned or passed.
In Swift, when you create a struct and assign it to a new variable, Swift makes a full copy. This means each variable has its own independent data. Example: struct Point { var x: Int var y: Int } var p1 = Point(x: 1, y: 2) var p2 = p1 p2.x = 10 print(p1.x) // 1 print(p2.x) // 10
Result
p1.x remains 1, p2.x changes to 10, showing they are separate copies.
Understanding that structs copy data helps prevent unexpected changes when you want independent values.
2
FoundationUnderstanding reference types with classes
🤔
Concept: Classes are reference types that share the same instance when assigned or passed.
In Swift, when you create a class and assign it to a new variable, both variables point to the same instance. Changing one affects the other. Example: class PointClass { var x: Int var y: Int init(x: Int, y: Int) { self.x = x self.y = y } } var c1 = PointClass(x: 1, y: 2) var c2 = c1 c2.x = 10 print(c1.x) // 10 print(c2.x) // 10
Result
Both c1.x and c2.x are 10, showing they share the same instance.
Knowing classes share references helps you manage shared state and avoid unintended side effects.
3
IntermediateWhen to choose structs over classes
🤔Before reading on: do you think structs are better for data that changes often or for simple, independent data? Commit to your answer.
Concept: Structs are best for simple, independent data that benefits from copying and safety.
Use structs when your data is small, represents a single value, or you want to avoid shared state bugs. Examples include points, sizes, or colors. Structs also work well with Swift’s protocols and value semantics, making your code safer and easier to reason about.
Result
Choosing structs leads to safer code with fewer bugs from shared data.
Understanding when data should be independent helps you pick structs to avoid complex bugs.
4
IntermediateWhen to choose classes over structs
🤔Before reading on: do you think classes are better for shared data or independent copies? Commit to your answer.
Concept: Classes are better when you need shared, mutable state or inheritance.
Use classes when you want multiple parts of your program to share and modify the same data. Classes also support inheritance, letting you create hierarchies of related types. Examples include UI elements, network connections, or data models that must be shared.
Result
Choosing classes enables shared state and object-oriented design.
Knowing when shared state is necessary helps you pick classes to model complex relationships.
5
IntermediateCopy-on-write optimization in structs
🤔
Concept: Swift uses copy-on-write to optimize struct copying for performance.
Copying large structs can be expensive. Swift uses a trick called copy-on-write: it only makes a real copy when you change the data, not when you assign it. Example: var array1 = [1, 2, 3] var array2 = array1 // No copy yet array2.append(4) // Copy happens here This keeps structs efficient while preserving value semantics.
Result
Structs behave like copies but avoid unnecessary memory use until modified.
Understanding copy-on-write explains how structs can be both safe and fast.
6
AdvancedMemory management differences between structs and classes
🤔Before reading on: do you think structs use reference counting like classes? Commit to your answer.
Concept: Classes use reference counting for memory management; structs do not.
Classes in Swift are managed by Automatic Reference Counting (ARC). Each reference increases a count, and when no references remain, the instance is freed. Structs are value types stored directly, so they don’t use ARC. This means structs are usually faster and simpler in memory but can use more space if copied often.
Result
Classes have overhead from ARC; structs have simpler, stack-based memory.
Knowing memory management differences helps optimize performance and avoid leaks.
7
ExpertUnexpected behavior with mutability and inheritance
🤔Before reading on: do you think structs can inherit from other structs or classes? Commit to your answer.
Concept: Structs cannot inherit; classes can. Mutability rules differ and affect behavior in subtle ways.
Structs do not support inheritance, so you cannot create a hierarchy. Classes support inheritance and polymorphism. Also, mutability differs: a struct instance is immutable if declared with let, but a class instance can have mutable properties even if the reference is let. Example: let s = Point(x: 1, y: 2) // s is immutable s.x = 10 // Error let c = PointClass(x: 1, y: 2) // c is immutable reference c.x = 10 // Allowed This can surprise developers expecting similar behavior.
Result
Structs and classes differ in inheritance and mutability, affecting design choices.
Understanding these subtle differences prevents bugs and guides correct API design.
Under the Hood
Structs are stored directly in memory where they are declared, often on the stack, and copied when assigned or passed. Classes are stored on the heap, and variables hold references (pointers) to the same instance. Swift uses ARC to track class references and free memory when no references remain. Copy-on-write optimizes large structs by delaying copying until mutation.
Why designed this way?
Swift’s design favors safety and performance. Value types (structs) prevent unintended shared state bugs and enable compiler optimizations. Reference types (classes) allow shared mutable state and object-oriented patterns. This dual system balances flexibility and safety, inspired by other languages but tailored for Swift’s goals.
┌───────────────┐          ┌───────────────┐
│   Stack       │          │    Heap       │
│ ┌─────────┐  │          │ ┌───────────┐ │
│ │ Struct  │  │          │ │  Class    │ │
│ │  Data   │  │          │ │  Instance │ │
│ └─────────┘  │          │ └───────────┘ │
└─────┬────────┘          └─────┬─────────┘
      │                         │
      │ copy                    │ reference
      ▼                         ▼
┌───────────────┐          ┌───────────────┐
│  New Struct   │          │  Reference    │
│   Copy       │          │  Variable     │
└───────────────┘          └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does assigning a struct to a new variable create a shared reference? Commit to yes or no.
Common Belief:Assigning a struct to a new variable creates a shared reference like a class.
Tap to reveal reality
Reality:Assigning a struct copies its data, so the new variable has an independent copy.
Why it matters:Believing structs share references can cause confusion and bugs when changes to one variable don't affect another.
Quick: Can structs inherit from other structs or classes? Commit to yes or no.
Common Belief:Structs can inherit from other structs or classes to reuse code.
Tap to reveal reality
Reality:Structs cannot inherit; only classes support inheritance in Swift.
Why it matters:Expecting inheritance with structs leads to design mistakes and compilation errors.
Quick: Does declaring a class instance with let make its properties immutable? Commit to yes or no.
Common Belief:Declaring a class instance with let makes all its properties immutable.
Tap to reveal reality
Reality:let makes the reference immutable, but class properties can still be changed if they are vars.
Why it matters:Misunderstanding this causes bugs when developers expect class properties to be constant but they change unexpectedly.
Quick: Are structs always slower than classes because they copy data? Commit to yes or no.
Common Belief:Structs are slower than classes because copying data is expensive.
Tap to reveal reality
Reality:Swift uses copy-on-write optimization for structs, making them efficient until mutated.
Why it matters:Assuming structs are slow can lead to overusing classes and losing safety benefits.
Expert Zone
1
Structs can conform to protocols with mutating methods, allowing controlled mutation while preserving value semantics.
2
Classes can cause retain cycles if references are not weak or unowned, leading to memory leaks, a subtle issue not present with structs.
3
Using structs with large data can still be efficient due to copy-on-write, but understanding when copies happen is crucial for performance tuning.
When NOT to use
Avoid structs when you need inheritance, shared mutable state, or identity semantics. Use classes instead. Conversely, avoid classes when you want thread-safe, independent copies or value semantics. Alternatives include enums for fixed sets of values or protocols for abstraction.
Production Patterns
In production Swift code, structs are commonly used for data models, configuration, and lightweight types. Classes are used for UI components, controllers, and shared resources. Developers often combine structs with protocols for flexible, safe designs and use classes when identity and inheritance are required.
Connections
Functional Programming
Builds-on
Understanding structs as value types connects to functional programming’s emphasis on immutable data and pure functions, helping write safer, side-effect-free code.
Memory Management
Builds-on
Knowing how structs and classes manage memory differently deepens understanding of ARC, stack vs heap allocation, and performance optimization.
Biology - Cell Replication
Analogy
Like cells copying their DNA to create independent organisms (structs), versus sharing resources in a colony (classes), this analogy helps grasp data copying vs shared references.
Common Pitfalls
#1Unexpected shared changes when using classes for independent data.
Wrong approach:class Person { var name: String init(name: String) { self.name = name } } var p1 = Person(name: "Alice") var p2 = p1 p2.name = "Bob" print(p1.name) // Outputs: Bob (unexpected)
Correct approach:struct Person { var name: String } var p1 = Person(name: "Alice") var p2 = p1 p2.name = "Bob" print(p1.name) // Outputs: Alice (expected)
Root cause:Confusing reference semantics of classes with value semantics of structs causes unintended shared mutations.
#2Trying to inherit from a struct to reuse code.
Wrong approach:struct Vehicle { var speed: Int } struct Car: Vehicle { // Error: structs cannot inherit var brand: String }
Correct approach:struct Vehicle { var speed: Int } struct Car { var vehicle: Vehicle var brand: String }
Root cause:Misunderstanding that structs do not support inheritance leads to compilation errors and poor design.
#3Assuming let makes class properties immutable.
Wrong approach:class Counter { var count = 0 } let c = Counter() c.count = 10 // Allowed, but unexpected if you think c is immutable
Correct approach:class Counter { private(set) var count = 0 func increment() { count += 1 } } let c = Counter() c.increment() // Controlled mutation
Root cause:Confusing immutability of references with immutability of the object’s properties.
Key Takeaways
Structs are value types that copy data, making them safe for independent data and avoiding shared state bugs.
Classes are reference types that share instances, enabling shared mutable state and inheritance but requiring careful memory management.
Choosing between structs and classes affects program behavior, performance, and safety, so understanding their differences is crucial.
Swift optimizes structs with copy-on-write to balance safety and efficiency, making them suitable for many use cases.
Knowing mutability rules and inheritance limits helps avoid common pitfalls and design better Swift programs.

Practice

(1/5)
1. Which statement best describes when to use a struct instead of a class in Swift?
easy
A. Use struct when you want to share data between multiple parts of your app.
B. Use struct when you need inheritance and shared references.
C. Use struct for simple data that should be copied when assigned or passed.
D. Use struct only for UI elements like views.

Solution

  1. Step 1: Understand struct behavior

    Structs are value types, meaning they are copied when assigned or passed around.
  2. Step 2: Compare with class behavior

    Classes are reference types, shared by multiple references and support inheritance.
  3. Final Answer:

    Use struct for simple data that should be copied when assigned or passed. -> Option C
  4. Quick Check:

    Structs = copied data [OK]
Hint: Structs copy data; classes share references [OK]
Common Mistakes:
  • Confusing structs with classes as reference types
  • Thinking structs support inheritance
  • Using structs for shared mutable state
2. Which of the following is the correct way to declare a struct in Swift?
easy
A. class Person { var name: String }
B. struct Person { var name: String }
C. struct Person: Class { var name: String }
D. class Person: struct { var name: String }

Solution

  1. Step 1: Recall struct declaration syntax

    In Swift, structs are declared with the keyword struct followed by the name and braces.
  2. Step 2: Check each option

    struct Person { var name: String } is correct. Using class is incorrect for structs. Structs cannot inherit from Class, and classes cannot inherit from struct.
  3. Final Answer:

    struct Person { var name: String } -> Option B
  4. Quick Check:

    Struct declaration = struct Name { } [OK]
Hint: Structs start with 'struct' keyword, not 'class' [OK]
Common Mistakes:
  • Using 'class' keyword to declare a struct
  • Trying to inherit from a class in a struct
  • Mixing struct and class syntax
3. Consider this Swift code:
struct Point {
  var x: Int
  var y: Int
}

var p1 = Point(x: 1, y: 2)
var p2 = p1
p2.x = 10
print(p1.x)

What is the output?
medium
A. 1
B. 10
C. 0
D. Compilation error

Solution

  1. Step 1: Understand struct copying behavior

    Assigning p1 to p2 copies the data because Point is a struct (value type).
  2. Step 2: Analyze mutation effect

    Changing p2.x to 10 does not affect p1.x, which remains 1.
  3. Final Answer:

    1 -> Option A
  4. Quick Check:

    Struct copy means original unchanged = 1 [OK]
Hint: Structs copy on assignment; original stays same [OK]
Common Mistakes:
  • Assuming p1.x changes when p2.x changes
  • Confusing structs with classes (reference types)
  • Expecting runtime error on mutation
4. This Swift code tries to share data between two variables:
class Counter {
  var count = 0
}

var c1 = Counter()
var c2 = c1
c2.count = 5
print(c1.count)

But the output is not as expected. What is the problem?
medium
A. Counter is a class, so c1 and c2 share the same instance
B. Counter is a class, but c2 is a copy, so changes don't reflect
C. Counter should be a struct to share data
D. Counter needs to conform to a protocol to share data

Solution

  1. Step 1: Identify class reference behavior

    Classes are reference types, so c1 and c2 point to the same object.
  2. Step 2: Understand shared mutation

    Changing c2.count to 5 changes the shared instance, so c1.count also becomes 5.
  3. Final Answer:

    Counter is a class, so c1 and c2 share the same instance -> Option A
  4. Quick Check:

    Classes share references = shared data [OK]
Hint: Classes share instances; structs copy data [OK]
Common Mistakes:
  • Thinking classes copy on assignment
  • Expecting structs to share data
  • Believing protocols affect sharing automatically
5. You are designing a Swift app that tracks user sessions. Each session has a unique ID and mutable state shared across multiple parts of the app. Which type should you choose and why?

Options:
A) Use a struct because it copies data and avoids bugs.
B) Use a class because it allows shared mutable state with identity.
C) Use a struct with inheritance to share state.
D) Use a class but avoid mutability to keep data safe.
hard
A. Use a struct because it copies data and avoids bugs.
B. Use a class but avoid mutability to keep data safe.
C. Use a struct with inheritance to share state.
D. Use a class because it allows shared mutable state with identity.

Solution

  1. Step 1: Analyze requirements for shared mutable state

    The session data must be shared and mutable across multiple parts, requiring a single shared instance.
  2. Step 2: Choose between struct and class

    Structs copy data, so changes in one copy won't reflect elsewhere. Classes share references and support identity, fitting the need.
  3. Step 3: Evaluate other options

    Structs do not support inheritance, so Use a struct with inheritance to share state. is invalid. Avoiding mutability in classes (Use a class but avoid mutability to keep data safe.) contradicts the requirement for mutable shared state.
  4. Final Answer:

    Use a class because it allows shared mutable state with identity. -> Option D
  5. Quick Check:

    Shared mutable state = class [OK]
Hint: Shared mutable data needs class, not struct [OK]
Common Mistakes:
  • Choosing struct for shared mutable state
  • Thinking structs support inheritance
  • Avoiding mutability in classes when needed
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard