Here are some commonly asked questions and answers on Struct vs. Class in Swift:
1. What is the primary difference between struct and class in Swift?
Answer:
The main difference is that structs are value types, whereas classes are reference types. This means:
- Structs are copied when assigned to a new variable or passed to a function.
- Classes share the same reference, meaning changes made to one instance affect all references to that instance.
2. When should you use a struct instead of a class?
Answer:
Use structs when:
- You need value-type behavior (independent copies).
- Your data is relatively simple and doesn’t require shared state or identity.
- You want thread safety without worrying about synchronization.
- You are using SwiftUI, as it prefers value types for performance.
Use classes when:
- You need reference-type behavior (shared state).
- You require inheritance.
- You want Objective-C interoperability (
@objc
features likeNSNotification
). - You need identity comparison (
===
operator).
3. Can structs have methods in Swift?
Answer:
Yes, structs in Swift can have methods just like classes. However, instance methods that modify properties must be marked with mutating
:
struct Car {
var speed: Int
mutating func increaseSpeed(by value: Int) {
speed += value
}
}
var myCar = Car(speed: 60)
myCar.increaseSpeed(by: 20)
print(myCar.speed) // Output: 80
4. Can structs conform to protocols?
Answer:
Yes, structs can conform to protocols just like classes:
protocol Drivable {
func drive()
}
struct Bike: Drivable {
func drive() {
print("Riding the bike")
}
}
5. Can structs have inheritance in Swift?
Answer:
No, structs do not support inheritance. However, they can conform to protocols, which allows them to share behavior.
6. Can a struct have a deinitializer (deinit
)?
Answer:
No, only classes can have deinit
because structs are value types and do not need deinitialization.
7. How does memory management differ between structs and classes?
Answer:
- Structs are stored in the stack and copied when assigned.
- Classes are stored in the heap, and references are managed using ARC (Automatic Reference Counting).
8. Can structs contain reference types like classes or closures?
Answer:
Yes, structs can contain reference types like classes or closures, but then the struct may exhibit reference-like behavior:
struct Container {
var object: SomeClass
}
If SomeClass
is a reference type, changes to object
will be reflected across copies of Container
.
9. Can structs be used with Swift’s Codable
protocol?
Answer:
Yes, structs can conform to Codable
easily for JSON encoding/decoding:
struct User: Codable {
var name: String
var age: Int
}
let user = User(name: "Mike", age: 30)
10. How can you make a class behave like a struct?
Answer:
To make a class behave more like a struct, use a copy-on-write pattern or implement a custom copy()
method.
Here are some advanced questions and answers on Struct vs. Class in Swift:
1. How does Copy-on-Write (CoW) work with Swift structs?
Answer:
Swift optimizes struct copies using Copy-on-Write (CoW). Instead of immediately copying, Swift defers the copy until a modification happens. This helps improve performance while maintaining value semantics.
Example:
struct CoWExample {
private var data = [1, 2, 3]
mutating func modifyData() {
data.append(4) // A copy happens here only if another reference exists
}
}
CoW ensures that structs do not duplicate data unless modified, reducing unnecessary memory overhead.
2. How does ARC (Automatic Reference Counting) behave differently for classes vs. structs?
Answer:
- Structs do not use ARC, as they are stored in the stack.
- Classes use ARC, which tracks references and automatically deallocates when no strong references remain.
Example with classes causing retain cycles:
class A {
var b: B?
}
class B {
var a: A?
}
var objA: A? = A()
var objB: B? = B()
objA?.b = objB
objB?.a = objA
// Memory leak: Both objects hold strong references to each other
To fix this, use weak references:
class B {
weak var a: A?
}
3. What happens when you pass a struct vs. a class to a function?
Answer:
- Structs (Value Types): A copy of the struct is passed. Changes inside the function do not affect the original struct.
- Classes (Reference Types): A reference is passed, so changes inside the function modify the original instance.
Example:
struct ValueType {
var x: Int
}
class ReferenceType {
var x: Int = 0
}
// Struct behavior
func modifyStruct(_ val: inout ValueType) {
val.x = 10
}
var myStruct = ValueType(x: 5)
modifyStruct(&myStruct)
print(myStruct.x) // 10 (copy modified)
// Class behavior
func modifyClass(_ ref: ReferenceType) {
ref.x = 10
}
var myClass = ReferenceType()
modifyClass(myClass)
print(myClass.x) // 10 (original modified)
4. How do you prevent unintended struct copying in Swift?
Answer:
Use @_copyable
(introduced in Swift 5.10) or store a reference type (like a class) inside the struct.
Example using a class inside a struct:
class ReferenceStorage {
var value: Int
init(value: Int) {
self.value = value
}
}
struct Wrapper {
var storage: ReferenceStorage
}
var a = Wrapper(storage: ReferenceStorage(value: 42))
var b = a
b.storage.value = 100
print(a.storage.value) // 100 (both share the same reference)
5. What are resilient structs in Swift, and why do they matter?
Answer:
A resilient struct is one where Swift allows future changes without breaking binary compatibility.
- If a struct is not resilient, its memory layout is fixed at compile-time.
- If it is resilient, the compiler allows new fields to be added without breaking ABI (Application Binary Interface).
Example of a non-resilient struct (default behavior within the same module):
struct NonResilient {
var a: Int
var b: String
}
Example of a resilient struct (needed when exposing via a framework):
@frozen struct Resilient {
var a: Int
}
-
@frozen
ensures the struct layout is fixed, allowing better optimization.
6. How can you manually implement Copy-on-Write behavior for a struct?
Answer:
By using an internal reference type and checking isKnownUniquelyReferenced()
:
final class InternalData {
var numbers: [Int]
init(numbers: [Int]) {
self.numbers = numbers
}
}
struct COWStruct {
private var data: InternalData
init(numbers: [Int]) {
self.data = InternalData(numbers: numbers)
}
mutating func modifyData() {
if !isKnownUniquelyReferenced(&data) {
data = InternalData(numbers: data.numbers) // Create a new copy
}
data.numbers.append(100)
}
}
This ensures that a new copy is created only when needed, reducing memory overhead.
7. Can structs have computed properties and property observers like classes?
Answer:
Yes, structs can have both:
struct Rectangle {
var width: Double
var height: Double
// Computed Property
var area: Double {
return width * height
}
// Property Observer
var perimeter: Double = 0.0 {
willSet {
print("About to set perimeter to \(newValue)")
}
didSet {
print("Updated perimeter to \(perimeter)")
}
}
}
8. How does performance differ between structs and classes in Swift?
Answer:
- Structs (stack allocation) are faster and use less memory for small, simple data types.
- Classes (heap allocation) require reference counting and can have ARC overhead when heavily used.
- Structs with CoW are optimized for efficiency.
Benchmarking Example:
import Foundation
class ClassObject {
var value: Int
init(value: Int) { self.value = value }
}
struct StructObject {
var value: Int
}
let start = CFAbsoluteTimeGetCurrent()
var objects = [StructObject(value: 0)]
for _ in 0..<1_000_000 {
objects.append(StructObject(value: 1))
}
print("Time taken:", CFAbsoluteTimeGetCurrent() - start)
- If you change
StructObject
toClassObject
, performance will decrease due to heap allocation and ARC.
9. How do Swift structs handle thread safety better than classes?
Answer:
Since structs are value types, each thread gets its own independent copy, making them naturally thread-safe.
Classes require synchronization to avoid race conditions:
class SharedResource {
private var value = 0
private let queue = DispatchQueue(label: "thread-safe", attributes: .concurrent)
func increment() {
queue.async(flags: .barrier) {
self.value += 1
}
}
}
With structs, there’s no need for synchronization since each thread has a separate copy.
10. Can you use structs with Objective-C interoperability (@objc
)?
Answer:
No, structs cannot be used with @objc
, as Objective-C does not support value types. You must use classes:
@objc class ObjCCompatible: NSObject {
@objc var name: String
@objc init(name: String) {
self.name = name
}
}
Here are some more advanced questions on Struct vs. Class in Swift:
1. What happens when you mutate a struct inside a closure?
Answer:
If a struct is captured by a closure, it behaves differently based on whether the closure is escaping or non-escaping.
- Non-escaping closure: The struct is passed directly, so mutations work as expected.
- Escaping closure: The struct is copied, and mutations do not affect the original instance.
Example:
struct Counter {
var value = 0
}
var counter = Counter()
let closure = { counter.value += 1 } // Works fine
closure()
print(counter.value) // Output: 1
func testEscaping(closure: @escaping () -> Void) {
closure()
}
testEscaping { counter.value += 1 } // Error: Cannot capture 'counter' mutably
To fix it, use inout
:
func testNonEscaping(closure: (inout Counter) -> Void) {
closure(&counter)
}
2. Can you use a class inside a struct? How does it affect mutability?
Answer:
Yes, a struct can contain a class instance. However, since classes are reference types, they break value-type behavior.
Example:
class Engine {
var horsepower: Int
init(horsepower: Int) {
self.horsepower = horsepower
}
}
struct Car {
var engine: Engine
}
var car1 = Car(engine: Engine(horsepower: 150))
var car2 = car1
car2.engine.horsepower = 200
print(car1.engine.horsepower) // Output: 200 (unexpected mutation!)
This happens because Car
is a value type, but engine
is a reference type.
To fix this, manually implement Copy-on-Write (CoW):
struct SafeCar {
private var engineRef: Engine
var engine: Engine {
mutating get {
if !isKnownUniquelyReferenced(&engineRef) {
engineRef = Engine(horsepower: engineRef.horsepower)
}
return engineRef
}
set { engineRef = newValue }
}
}
3. How can you enforce immutability in a class like a struct?
Answer:
Since structs are immutable by default, you can achieve similar behavior in classes using let
properties and making them final
:
final class ImmutableClass {
let value: Int
init(value: Int) {
self.value = value
}
}
This prevents modifications after initialization, just like a struct.
4. What happens when you use a struct inside a collection (e.g., Array)?
Answer:
When a struct is inside an Array
, every time it is modified, a new copy is created due to Copy-on-Write (CoW).
Example:
struct Item {
var value: Int
}
var items = [Item(value: 1), Item(value: 2)]
var copy = items // CoW happens only when modified
copy[0].value = 100
print(items[0].value) // Output: 1
To prevent unnecessary copying, Swift uses Copy-on-Write (CoW) under the hood.
5. Can you override methods in a struct?
Answer:
No, structs do not support method overriding because they do not support inheritance.
However, you can achieve similar behavior using protocols and protocol extensions:
protocol Drawable {
func draw()
}
struct Circle: Drawable {
func draw() {
print("Drawing a circle")
}
}
6. What are the performance trade-offs of using structs over classes?
Answer:
✅ Structs (Value Types) are Faster
- Stored in the stack, leading to faster allocation & deallocation.
- No ARC (Automatic Reference Counting) overhead.
- Better cache locality, improving performance in large data structures.
❌ Classes (Reference Types) Have More Overhead
- Stored in the heap, leading to slower access.
- ARC overhead due to reference counting.
- Shared across multiple objects, which may introduce race conditions.
7. How can you prevent struct copies in a function?
Answer:
Use inout
to modify the original struct instance without copying:
struct DataStore {
var value: Int
}
func modify(_ data: inout DataStore) {
data.value += 10
}
var data = DataStore(value: 5)
modify(&data)
print(data.value) // Output: 15
Without inout
, a copy is made.
8. Can structs conform to Equatable
and Hashable
?
Answer:
Yes, and Swift automatically synthesizes Equatable
and Hashable
for structs:
struct Point: Equatable, Hashable {
var x: Int
var y: Int
}
let p1 = Point(x: 3, y: 4)
let p2 = Point(x: 3, y: 4)
print(p1 == p2) // Output: true
For custom comparison, implement ==
manually:
struct CustomPoint {
var x: Int
var y: Int
static func == (lhs: CustomPoint, rhs: CustomPoint) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
9. How does protocol-oriented programming (POP) relate to structs?
Answer:
Swift encourages protocol-oriented programming (POP) by using structs instead of classes.
- Since structs don’t support inheritance, Swift relies on protocols to share behavior.
- Protocol extensions allow adding default functionality.
Example:
protocol Identifiable {
var id: String { get }
}
extension Identifiable {
func printID() {
print("ID: \(id)")
}
}
struct User: Identifiable {
var id: String
}
let user = User(id: "12345")
user.printID() // Output: ID: 12345
This eliminates the need for class-based inheritance, making code more modular.
10. How do Swift structs behave differently in SwiftUI?
Answer:
SwiftUI relies heavily on structs because they:
- Ensure immutability, making UI updates safer.
- Avoid reference-type race conditions (since structs don’t share state).
- Work well with declarative syntax.
Example of SwiftUI View as a struct:
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, World!")
}
}
Since ContentView
is a struct, every time SwiftUI needs a new UI update, it simply creates a new copy—no ARC needed!
Bonus: How can you convert a class-based model to a struct-based model safely?
Answer:
If you want to convert a class to a struct, check:
✅ Does the object need identity? → If yes, keep it a class.
✅ Does it require reference semantics? → If yes, keep it a class.
✅ Does it only store data? → Convert it to a struct.
Example Class to Struct Migration:
// Old Class
class User {
var name: String
init(name: String) {
self.name = name
}
}
// New Struct
struct User {
var name: String
}
This ensures better performance and thread safety