Swift ARC and memory management interview questions, with answers
Swift frees memory with automatic reference counting rather than a garbage collector, which makes deallocation predictable but puts one responsibility on the programmer: avoiding reference cycles. iOS interviews almost always include a memory question, usually about closures capturing self.
The answers below cover how ARC works, how it differs from garbage collection, reference cycles, [weak self], value types and finding leaks, with code compiled with swiftc 6.4. Then take the free Swift diagnostic — ten questions across every Swift topic in the bank — to see which of these you can explain but not yet predict.
The questions, with answers
1.What is ARC in Swift?
In short: Automatic reference counting: Swift counts strong references to each class instance and frees it, calling deinit, when the count reaches zero.
Every class instance has a reference count. Assigning it to a variable, constant or property creates a strong reference and increments the count, and when a reference goes away, because a variable goes out of scope or is set to nil, the count drops. When it reaches zero, Swift runs the instance's deinit and frees its memory immediately. The compiler inserts the retain and release operations itself. In the code the File instance lives only inside the do block, so it is closed before end is printed.
final class File { let name: String init(_ n: String) { name = n print("open \(n)") } deinit { print("close \(name)") } } do { let f = File("a.txt") print("using \(f.name)") } print("end") // open a.txt // using a.txt // close a.txt // end
2.How is ARC different from garbage collection?
In short: ARC frees an object the moment its last reference goes, with no collector pauses, but it cannot reclaim reference cycles on its own.
A tracing garbage collector, as in Java or C#, periodically finds objects that can no longer be reached from the program's roots and frees them, which handles cycles automatically but frees memory at unpredictable times and can pause the program. ARC does its work inline: each object is freed as soon as its reference count reaches zero, so memory use is steady and deinit runs at a predictable point, which suits phones and real-time interfaces. The trade-off is that objects referring to each other in a cycle keep each other's counts above zero forever unless the cycle is broken with weak or unowned references.
3.What is a strong reference cycle in Swift?
In short: Two or more objects holding strong references to each other, directly or through closures, so none of their counts reaches zero and they leak.
ARC frees an object only when nothing refers to it strongly. If an object stores a closure that captures self strongly, the object owns the closure and the closure owns the object, so even after every outside reference is gone, each keeps the other alive. In the code, Page stores a closure that calls self.draw(), so setting p to nil does not free the page and deinit never prints. Leaks like this grow every time the screen is opened, which is why closures stored on objects deserve a second look in review.
final class Page { var cb: (() -> Void)? func setUp() { cb = { self.draw() } } func draw() {} deinit { print("freed") } } var p: Page? = Page() p?.setUp() p = nil print("end") // end
4.How does [weak self] prevent a reference cycle in a Swift closure?
In short: It captures self weakly, so the closure does not keep the object alive, and self inside the closure becomes an optional.
Adding a capture list with weak self changes how the closure holds the object: the reference no longer counts, so the object can be freed when its other references go away, and self inside the closure is an optional that becomes nil at that point. The closure then calls methods with self?. or unwraps with guard let self. In the code, the same Page as before is now freed as soon as p is set to nil. [weak self] is the standard pattern for escaping closures stored by, or outliving, the object that creates them.
final class Page { var cb: (() -> Void)? func setUp() { cb = { [weak self] in self?.draw() } } func draw() {} deinit { print("freed") } } var p: Page? = Page() p?.setUp() p = nil print("end") // freed // end
5.Does ARC apply to structs and enums in Swift?
In short: Not directly: value types are copied, not reference counted, but any class instances they contain are still counted.
Reference counting applies to class instances, the only values with shared identity. Structs and enums are copied, so they have no reference count of their own and no deinit. A struct that stores a class instance, however, holds a strong reference to it, and every copy of the struct adds another, so the instance lives as long as any copy does, which is why Img below is not freed when c is set to nil: copy still holds it. Arrays and strings manage their internal buffers with reference counting under the hood to make copying cheap.
final class Img { deinit { print("freed") } } struct Card { let img = Img() } var c: Card? = Card() let copy = c c = nil print("still held") // still held
6.How do you find memory leaks in a Swift app?
In short: Use Xcode's Memory Graph Debugger and the Leaks and Allocations instruments, and check that deinit runs when screens close.
Xcode's Debug Memory Graph pauses the app and shows every live object and the references between them, highlighting leaked cycles, which makes it the quickest way to find which closure or property holds an object. Instruments' Leaks template detects unreachable memory while the app runs, and Allocations shows objects that keep growing as you repeat an action, such as opening and closing a screen. A simple habit catches many leaks early: log or break in deinit of view controllers and view models, and confirm it runs when the screen is dismissed.
How the diagnostic asks it
One question from the Swift bank, exactly as a sitting would show it. The bank has 4 on memory & arc and 30 across Swift.
What does this Swift code print?
class Temp { deinit { print("freed") } } var t: Temp? = Temp() print("before") t = nil print("after")
- 1before, freed, then aftercorrect
- 2before, after, then freed
- 3freed, before, then after
- 4before, then after
Swift manages class instances with automatic reference counting. t holds the only strong reference, so setting t to nil drops the count to zero and the object is freed immediately, running deinit, before the next line. The output is before, freed, then after. before, after, then freed describes a tracing garbage collector, as in Java, which frees objects at some later point. freed first assumes the object dies at creation. before, then after assumes deinit never runs, which happens only when a reference cycle keeps the object alive.
Measure it
Reading answers tells you what’s true. A diagnostic tells you what you get wrong.
10 Swift questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.