I Thought 'Struct vs Class' Was Easy, Until I Failed an Interview Question.

Way back then, when I thought I understood this topic, I interviewed for an iOS Engineer position that I really wanted. I felt prepared. I had reviewed my algorithms, design patterns, and the lifecycle of a View Controller.
The interview started well. But then, the interviewer asked a question that seemed simple on the surface, the classic “Struct vs Class” debate.
I confidently replied: “Structs are value types, Classes are reference types.”
The interviewer nodded. “Correct. But let’s dig deeper. What happens when you mix them?”
That was the moment I realized I didn’t actually understand memory management as well as I thought I did. I stumbled through the answer, and got it wrong (spoiler: I didn’t get the job).
But that failure forced me to go back and really understand what Swift is doing with memory. Here is the deep technical breakdown of the question that stumped me.
The Question
The interviewer gave me two scenarios involving nested types:
-
A Struct that holds a reference to a Class.
-
A Class that holds a Struct.
He asked: When we assign these to new variables, what gets copied? What stays shared?
Let’s break down exactly why this is tricky.
Scenario 1: The Struct holding a Class
The Setup: You have a struct (Value Type). Inside it, there is a property that points to a class (Reference Type).
The Trap: Because the parent is a struct, we assume it is a “Value Type”, so it should be fully independent when copied. We expect structB to be totally separate from structA.
The Reality: Swift performs a shallow copy.
When you assign the struct to a new variable, Swift copies the struct container. However, for the property that holds the class, Swift only copies the pointer (the memory address), not the actual object on the heap.
The Code Proof
To prove this, we can use the Unmanaged helper in Swift to print the actual memory address of the class instance.
import Foundation
// 1. The Class (Reference Type)
class Heart {
var bpm: Int = 60
}
// 2. The Struct (Value Type) holding the Class
struct Person {
var name: String
var heart: Heart // <--- The reference type property
}
// --- execution ---
// Create the first person
let myHeart = Heart()
var personA = Person(name = "Alice", heart = myHeart)
// Create personB by assigning personA
// Since Person is a struct, 'personA' is COPIED to create 'personB'.
var personB = personA
// Let's look at the memory addresses of the 'heart' property
let addressA = Unmanaged.passUnretained(personA.heart).toOpaque()
let addressB = Unmanaged.passUnretained(personB.heart).toOpaque()
print("Address of personA.heart: \(addressA)")
print("Address of personB.heart: \(addressB)")
// Result: They print the EXACT SAME memory address.
// Now, the danger zone:
personB.heart.bpm = 120
// We changed personB... but look what happened to personA:
print("Alice's heart rate: \(personA.heart.bpm)")
// Output: 120
Why did I fail this?
I forgot that a struct doesn’t magically convert reference types inside it into value types.
Think of the struct as a physical envelope. Inside the envelope, there is a piece of paper with an address written on it (the Class reference). When I copy the struct, I get a new envelope (a new struct instance), and I photocopy the paper inside.
I now have two envelopes, but the address written on the paper inside still points to the same house.
Scenario 2: The Class holding a Struct
The Setup: You have a class (Reference Type). Inside it, there is a struct (Value Type).
The Trap: We know structs are “copy-on-assignment.” So, if I assign the class to a new variable, does the struct inside get copied?
The Reality: Nothing gets copied.
Classes have reference semantics. When you assign a class instance to a new variable, you are not creating a new object. You are just creating a second reference (a second pointer) to the same object in memory. Since you didn’t create a new object, the struct inside it stays exactly where it is.
The Code Proof
// 1. The Struct (Value Type)
struct Battery {
var charge: Int = 100
}
// 2. The Class (Reference Type) holding the Struct
class Phone {
var model: String
var battery: Battery // <--- The value type property
init(model: String) {
self.model = model
self.battery = Battery()
}
}
// --- execution ---
// Create the first phone
let phoneA = Phone(model = "iPhone 15")
// Assign to a new variable
// Because Phone is a class, NO COPY is made.
// We just created a new pointer to the same memory allocation.
let phoneB = phoneA
// Check if they are the same instance
print(phoneA === phoneB)
// Output: true
// Mutate the struct via phoneB
phoneB.battery.charge = 50
// Check phoneA
print("Phone A charge: \(phoneA.battery.charge)")
// Output: 50
The Explanation
In this case, phoneA and phoneB are like two remote controls paired to the same TV.
The struct (the battery) is inside the TV. It doesn’t matter which remote you use to change the channel (or drain the battery), you are interacting with the single, shared instance of the TV. The struct was never copied because the container (the Class) was never copied.
The Technical Summary (TL;DR)
If you are ever asked this in an interview, here is the technical summary that I wish I had given:
-
Structs allow “Shared Mutable State” if they hold References. Even though a Struct is a value type, if it holds a reference to a Class, that reference is shared across copies. This is often called a “Violated Value Semantics.”
-
Classes share everything, including their Structs. When multiple variables point to the same Class instance, they all share the data inside that class, regardless of whether that data is a
String,Int, orStruct. -
Deep Copies don’t happen automatically. In Swift, assigning a struct does a bitwise copy of the values stored in the struct. If one of those values is a memory address (a reference), only the address is copied, not the object it points to. To get a truly unique copy, you would need to manually implement a “Deep Copy” strategy.
Conclusion
It stings a little to look back at that interview. It seems so obvious now, but under the pressure of a video call, my brain just froze on the textbook definitions: “Struct = Stack, Class = Heap.”
I didn’t get the offer, but I learned a valuable lesson:
Definitions are useless if you can’t visualize the memory graph.
Hopefully, this explanation saves you from making the same mistake I did.