SwiftMidMCQ
What are the most common causes of memory leaks in iOS applications?
Test your knowledge:
Explanation & Code
Answer: The most common causes of memory leaks in iOS are strong reference cycles between classes, closure capture retention, un-invalidated Timers, retained Combine subscriptions, and unmanaged Core Foundation allocations.
A memory leak occurs when heap-allocated memory is no longer needed by the application but cannot be freed by ARC because its reference count remains greater than zero.
- Retain Cycles in Class Hierarchies: Two objects holding strong references to each other (e.g. parent coordinator holding child view model while child holds strong parent reference).
- Closure Self-Capture: Stored closures or async callbacks capturing
selfstrongly without[weak self]. - Non-Weak Delegates: Delegate protocols declared without
: AnyObjectand delegate properties declared withoutweak. - Target-Action Timers & CADisplayLink:
Timer.scheduledTimerholding a strong reference to its target object in theRunLoop. - Combine Subscriptions: Storing an
AnyCancellableset onselfwhile the.sinkclosure capturesselfstrongly. - NotificationCenter Observers: Block-based observers added with
addObserver(forName:...)whose token is retained byselfwhile the block strongly retainsself. - Singletons and Global Managers: Registering closures or listeners to singleton instances without weak capture.
- Core Foundation / C-APIs: Failing to balance
CFRetain/passRetainedwithCFRelease/takeRetainedValue.
Code Example:
// Common Leak Scenario 1: Retained Delegate
protocol NetworkServiceDelegate: AnyObject { // Must be AnyObject!
func dataDidUpdate()
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // weak avoids retain cycle
}
// Common Leak Scenario 2: Stored Closure Retain Cycle
class ProfileViewModel {
var onAvatarLoaded: (() -> Void)?
var avatarImage: String = "default.png"
func setupBindings() {
// ❌ Leaks if self holds onAvatarLoaded and onAvatarLoaded holds self
// onAvatarLoaded = { self.avatarImage = "new.png" }
// ✅ Safe with [weak self]
onAvatarLoaded = { [weak self] in
self?.avatarImage = "new.png"
}
}
}
Key Points:
- A single leaked ViewController retains its entire subview hierarchy, image buffers, and child view models
- Memory leaks lead to memory pressure warnings, background termination, and Jetsam OOM (Out Of Memory) crashes
- Always check object ownership: parents own children strongly; children reference parents weakly
Rate your understanding: