Lesson 69Classes
Class vs Struct
The Big Picture
Choosing between class and struct is one of the most important decisions in Swift. Understanding their differences helps you write better, more efficient code!
Key Differences
| Feature | Struct | Class |
|---|---|---|
| Type | Value Type | Reference Type |
| Inheritance | No | Yes |
| Deinitializer | No | Yes |
| Identity (===) | No | Yes |
| Memberwise Init | Automatic | Manual |
| let Mutability | Fully immutable | Properties mutable |
When to Use
Use Struct When:
- Simple data containers
- No inheritance needed
- Value semantics preferred
- Thread safety matters
- Copying is logical
Use Class When:
- Inheritance required
- Identity matters (===)
- Shared mutable state
- Deinitializer needed
- Objective-C interop
Apple's Recommendation
"Start with structs by default!"
Use classes only when you specifically need reference semantics or class-only features like inheritance.
Copy-on-Write
Swift's standard library collections (Array, Dictionary, Set) use Copy-on-Write - they only copy data when you actually modify the copy, making them efficient despite being value types!
main.swift
// CLASS VS STRUCT
// Understanding when to use each
// STRUCT - Value Type
struct PointStruct {
var x: Double
var y: Double
}
var point1 = PointStruct(x: 10, y: 20)
var point2 = point1 // COPY is made
point2.x = 100
print(point1.x) // 10 (unchanged!)
print(point2.x) // 100
// CLASS - Reference Type
class PointClass {
var x: Double
var y: Double
init(x: Double, y: Double) {
self.x = x
self.y = y
}
}
var pointA = PointClass(x: 10, y: 20)
var pointB = pointA // Same reference
pointB.x = 100
print(pointA.x) // 100 (changed!)
print(pointB.x) // 100
// ===== KEY DIFFERENCES =====
// 1. INHERITANCE
// Classes support inheritance
class Animal {
func speak() { print("...") }
}
class Dog: Animal {
override func speak() { print("Woof!") }
}
// Structs DON'T support inheritance
struct Cat {
func speak() { print("Meow!") }
}
// struct Kitten: Cat { } // ERROR!
// 2. IDENTITY CHECKING
let classA = PointClass(x: 0, y: 0)
let classB = classA
let classC = PointClass(x: 0, y: 0)
print(classA === classB) // true (same instance)
print(classA === classC) // false (different instances)
// Structs don't have identity
// point1 === point2 // ERROR! === only for classes
// 3. MUTABILITY
let constantStruct = PointStruct(x: 1, y: 2)
// constantStruct.x = 10 // ERROR! Can't mutate let struct
let constantClass = PointClass(x: 1, y: 2)
constantClass.x = 10 // OK! Reference is constant, not the data
// 4. DEINITIALIZERS
class Resource {
deinit {
print("Cleanup!") // Only classes have deinit
}
}
// 5. COPY BEHAVIOR IN FUNCTIONS
func modifyStruct(_ point: PointStruct) {
var localPoint = point
localPoint.x = 999 // Only changes local copy
}
func modifyClass(_ point: PointClass) {
point.x = 999 // Changes original!
}
var structPoint = PointStruct(x: 0, y: 0)
modifyStruct(structPoint)
print(structPoint.x) // 0 (unchanged)
var classPoint = PointClass(x: 0, y: 0)
modifyClass(classPoint)
print(classPoint.x) // 999 (changed!)
// ===== WHEN TO USE STRUCT =====
// - Simple data containers
// - No need for inheritance
// - Value semantics make sense
// - Thread-safe by default
struct User {
var name: String
var email: String
var age: Int
}
struct Rectangle {
var width: Double
var height: Double
var area: Double {
return width * height
}
}
// ===== WHEN TO USE CLASS =====
// - Need inheritance
// - Need identity (===)
// - Need deinitializers
// - Shared mutable state
// - Interop with Objective-C
class ViewController {
var title: String
var isLoading: Bool = false
init(title: String) {
self.title = title
}
func loadData() {
isLoading = true
// ... load data
isLoading = false
}
}
class NetworkManager {
static let shared = NetworkManager() // Singleton
private init() { }
func fetch(url: String) {
print("Fetching: \(url)")
}
}
// ===== PERFORMANCE CONSIDERATIONS =====
// Structs: Stack allocated (usually faster)
// Classes: Heap allocated + reference counting
// BUT: Large structs might be slower to copy
// Swift uses Copy-on-Write for standard library types
var array1 = [1, 2, 3, 4, 5]
var array2 = array1 // No copy yet!
array2.append(6) // Copy happens hereTry It Yourself!
Think about a real-world entity (like a BankAccount). Would you model it as a struct or class? What are the trade-offs? Implement both and observe the behavioral differences!