Class Vs Struct
Heap Memory vs Stack Memory
Meaning of Reference Types vs Value Types:
Reference types (e.g., classes) are stored in heap memory, and when you assign an instance of a class to another variable, both variables reference the same object in memory. ARC (Automatic Reference Counting) is used to manage memory for these objects, counting how many references exist to each object and deallocating it when no references remain.
Value types (e.g., structs, enums) are stored in stack memory. When a value type is assigned to another variable or passed to a function, a copy of the data is made. Each variable holds its own unique copy, so no reference counting is necessary.
Use Classes:
- When you need shared state or reference semantics.
- When inheritance is necessary.
- When you need instances that should be uniquely identifiable.
Use Structs:
- When you need value semantics — independent copies of the data.
- When working with simple data that doesn’t require shared mutable state.
- When performance matters for small data models.
Class is inherently not thread safe but struct is a thread safe below sample examples.
Get Pradeep Kumar’s stories in your inbox
Join Medium for free to get updates from this writer.
Class & Struct Examples:
Example #1
class CounterStruct {
var value = 0
mutating func increment() {
value += 1
}
func getValue() -> Int {
return value
}
}
var counter1 = CounterStruct()
var counter2 = counter1 // Copy the struct
// Simulating concurrent access in separate threads
DispatchQueue.global().async {
for _ in 0..<1000 {
var localCounter = counter1
localCounter.increment()
counter1 = localCounter
}
}
DispatchQueue.global().async {
for _ in 0..<1000 {
var localCounter = counter2
localCounter.increment()
counter2 = localCounter
}
}
sleep(1)
print("Final value of struct counter1: \(counter1.getValue())")
print("Final value of struct counter2: \(counter2.getValue())")
Examples #2:
// Thread Safe Code
class ThreadSafeClass {
private var value: Int = 0
private let queue = DispatchQueue(label: "thread-safe-queue")
// Thread-safe method to increment the value
func increment() {
queue.sync {
value += 1
}
}
// Thread-safe method to get the value
func getValue() -> Int {
return queue.sync {
return value
}
}
}
let threadSafeObject = ThreadSafeClass()
// Simulating concurrent access by multiple threads
DispatchQueue.global().async {
for _ in 0..<1000 {
threadSafeObject.increment()
}
}
DispatchQueue.global().async {
for _ in 0..<1000 {
threadSafeObject.increment()
}
}
// Delay to ensure both threads complete execution
sleep(1)
print("Final value: \(threadSafeObject.getValue())")Copy on Write (CoW)
- Definition: Copy on Write is an optimization technique used in Swift for value types (like arrays and dictionaries) where a copy of the data is made only when it is modified.
- How It Works:
- When you assign or pass a value type, Swift doesn’t immediately create a copy.
- Both references share the same data.
- A copy is created only when one of the references modifies the data, ensuring efficient memory use.
var array1 = [1, 2, 3]
var array2 = array1 // Shared data, no copy yet
array2.append(4) // Copy created here for array2Copy on Read (CoR)
- Definition: Copy on Read is a technique where a copy of the data is created when it is read, often used to ensure safety in concurrent scenarios.
- How It Works:
- When multiple threads access the same data, each thread gets a separate copy upon reading.
- This prevents unintended mutations and ensures thread safety.
class SharedData {
private var data: [Int]
private let lock = NSLock()
init(data: [Int]) {
self.data = data
}
func readData() -> [Int] {
lock.lock()
defer { lock.unlock() }
return data // Returns a copy of the data
}
func writeData(_ newData: [Int]) {
lock.lock()
defer { lock.unlock() }
data = newData
}
}Shallow Copy & Deep Copy:
- Shallow Copy: “A shallow copy creates a new reference to the same object or data in memory, meaning that both the original and the copied object share the same memory space. Any changes made to the copied object will reflect in the original, and vice versa.”
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let person1 = Person(name: "Pradeep")
let person2 = person1 // Shallow copy
person2.name = "Kumar"
print(person1.name) // Output: "Kumar"- Deep Copy: “A deep copy, on the other hand, creates a completely independent copy of the object, duplicating the entire structure. Any changes made to the copied object will not affect the original one since they now refer to separate instances in memory.”
let person1 = Person(name: "Pradeep")
let person2 = Person(name: person1.name) // Deep copy
person2.name = "Kumar"
print(person1.name) // Output: "Pradeep"Shallow and Deep Copy with Classes (Reference Types)
- Classes in Swift are reference types, meaning they follow shallow copy by default. When you assign a class instance to a new variable or pass it to a function, Swift only copies the reference to the object, not the object itself. Both variables point to the same instance in memory.
- Deep copy with classes requires manual implementation if you need an independent copy of the object.
Shallow Copy Example (Classes):
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let person1 = Person(name: "Pradeep")
let person2 = person1 // Shallow copy, both point to the same object
person2.name = "Kumar"
print(person1.name) // Output: "Kumar" (because both references point to the same instance)Deep Copy Example (Classes):
To create a deep copy of a class, you would typically implement a method that manually copies all the properties of the class into a new instance:
class Person {
var name: String
init(name: String) {
self.name = name
}
// Deep copy method
func deepCopy() -> Person {
return Person(name: self.name)
}
}
let person1 = Person(name: "Pradeep")
let person2 = person1.deepCopy() // Deep copy, creates a new instance
person2.name = "Kumar"
print(person1.name) // Output: "Pradeep" (unchanged because of deep copy)Shallow and Deep Copy with Structs (Value Types)
- Structs in Swift are value types, which means they inherently perform a deep copy when assigned or passed between variables.
- Every time you assign a struct to a new variable or pass it to a function, Swift creates a new, independent copy of the entire struct. Changes to one copy do not affect the other. This behavior is automatic and does not require manual intervention.
struct Point {
var x: Int
var y: Int
}
var point1 = Point(x: 10, y: 20)
var point2 = point1 // A deep copy is made
point2.x = 50
print(point1.x) // Output: 10 (unchanged, because it's a deep copy)Shallow Copy Behaviour in Struct:
class Car {
var model: String
init(model: String) {
self.model = model
}
}
struct Person {
var name: String
var car: Car
}
var car1 = Car(model: "Tesla")
var person1 = Person(name: "Avinash", car: car1)
// Copy the struct (deep copy of the value type, but shallow copy of the reference type inside)
var person2 = person1
// Change the car's model in person2
person2.car.model = "Audi"
// Both person1 and person2 point to the same Car instance
print(person1.car.model) // Output: "Audi" (shallow copy for reference type inside the struct)
print(person2.car.model) // Output: "Audi"




