0
0
Swiftprogramming~15 mins

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

Choose your learning style9 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.