Bird
Raised Fist0
Swiftprogramming~20 mins

Closures causing retain cycles in Swift - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Swift Closure Mastery
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ Predict Output
intermediate
2:00remaining
What is the output of this Swift code with a closure?

Consider this Swift class with a closure property capturing self. What will be printed when example.run() is called?

Swift
class Example {
    var value = 10
    lazy var closure: () -> Void = {
        print("Value is \(self.value)")
    }
    func run() {
        closure()
    }
}

let example = Example()
example.run()
ACompilation error due to missing self capture
BValue is 0
CRuntime error due to retain cycle
DValue is 10
Attempts:
2 left
💡 Hint

Think about how self is captured in the closure and what value is initialized to.

❓ Predict Output
intermediate
2:00remaining
What happens when a closure strongly captures self causing a retain cycle?

Given this Swift code, what will be the output or behavior?

Swift
class Person {
    let name: String
    lazy var greet: () -> Void = {
        print("Hello, \(self.name)!")
    }
    init(name: String) {
        self.name = name
    }
    deinit {
        print("Person \(name) is being deinitialized")
    }
}

var person: Person? = Person(name: "Alice")
person?.greet()
person = nil
AHello, Alice!\nPerson Alice is being deinitialized
BHello, Alice!\n(no deinit message, memory leak occurs)
CCompilation error due to lazy var closure
DRuntime crash due to retain cycle
Attempts:
2 left
💡 Hint

Think about how the closure captures self and what happens when person is set to nil.

🔧 Debug
advanced
2:00remaining
Identify the cause of the retain cycle in this Swift code

Examine the following Swift code. Why does the ViewController instance never get deinitialized?

Swift
class ViewController {
    var completionHandler: (() -> Void)?
    func setup() {
        completionHandler = {
            print("ViewController is running")
            self.doSomething()
        }
    }
    func doSomething() {
        print("Doing something")
    }
    deinit {
        print("ViewController deinitialized")
    }
}

var vc: ViewController? = ViewController()
vc?.setup()
vc = nil
AThe closure captures self strongly causing a retain cycle
BThe closure is not called so deinit is delayed
CThe completionHandler is nil so no retain cycle
DThe doSomething method causes a runtime error
Attempts:
2 left
💡 Hint

Think about how closures capture variables and how that affects object lifetime.

📝 Syntax
advanced
2:00remaining
Which option correctly breaks the retain cycle with a weak capture?

Given the retain cycle in the previous example, which closure syntax correctly uses a weak capture of self to avoid the cycle?

Swift
class Controller {
    var handler: (() -> Void)?
    func configure() {
        handler = { /* capture self weakly here */
            self?.action()
        }
    }
    func action() {
        print("Action executed")
    }
    deinit {
        print("Controller deinitialized")
    }
}
Ahandler = { [weak self] in self?.action() }
Bhandler = { [unowned self] in self.action() }
Chandler = { [self] in self.action() }
Dhandler = { in self?.action() }
Attempts:
2 left
💡 Hint

Consider how to safely capture self without causing a retain cycle.

🚀 Application
expert
3:00remaining
How many objects remain in memory after this code runs?

Analyze this Swift code snippet. After manager = nil, how many Task instances remain in memory?

Swift
class Task {
    let id: Int
    init(id: Int) { self.id = id }
    deinit { print("Task \(id) deinitialized") }
}

class Manager {
    var tasks: [Task] = []
    lazy var taskHandler: () -> Void = { [weak self] in
        guard let self else { return }
        for task in self.tasks {
            print("Handling task \(task.id)")
        }
    }
    func addTask(_ task: Task) {
        tasks.append(task)
    }
    deinit { print("Manager deinitialized") }
}

var manager: Manager? = Manager()
manager?.addTask(Task(id: 1))
manager?.addTask(Task(id: 2))
manager?.taskHandler()
manager = nil
A2 tasks remain; tasks are strongly held by manager
B1 task remains due to partial retain cycle
C0 tasks remain; all deinitialized
DRuntime error due to weak self capture
Attempts:
2 left
💡 Hint

Think about ownership and how weak capture affects object lifetime.

Practice

(1/5)
1. What is the main reason closures cause retain cycles in Swift?
easy
A. Closures capture self strongly, preventing deallocation
B. Closures do not capture variables at all
C. Closures always run on a background thread
D. Closures automatically break retain cycles

Solution

  1. Step 1: Understand closure capturing behavior

    Closures in Swift capture variables, including self, strongly by default.
  2. Step 2: Recognize impact on memory

    If a closure captures self strongly and self holds the closure, neither can be released, causing a retain cycle.
  3. Final Answer:

    Closures capture self strongly, preventing deallocation -> Option A
  4. Quick Check:

    Strong capture causes retain cycle = B [OK]
Hint: Closures keep strong references by default, causing retain cycles [OK]
Common Mistakes:
  • Thinking closures do not capture variables
  • Assuming closures run on background threads
  • Believing closures automatically break retain cycles
2. Which of the following is the correct syntax to declare a closure capturing self weakly in Swift?
easy
A. let closure = { weak self in print(self) }
B. let closure = { (weak self) -> Void in print(self) }
C. let closure = { [weak self] in print(self) }
D. let closure = { [self weak] in print(self) }

Solution

  1. Step 1: Recall capture list syntax

    In Swift, capture lists are declared inside square brackets before the closure body.
  2. Step 2: Identify correct weak capture syntax

    The correct syntax is [weak self] inside the capture list, followed by the closure code.
  3. Final Answer:

    let closure = { [weak self] in print(self) } -> Option C
  4. Quick Check:

    Capture list uses [weak self] = C [OK]
Hint: Use square brackets for capture lists like [weak self] [OK]
Common Mistakes:
  • Using parentheses instead of brackets for capture list
  • Writing weak self without brackets
  • Swapping order to [self weak]
3. Consider this Swift code snippet:
class MyClass {
    var closure: (() -> Void)?
    func setup() {
        closure = {
            print(self)
        }
    }
}
let obj = MyClass()
obj.setup()
What is the main problem with this code?
medium
A. It runs without any memory issues
B. It causes a syntax error due to missing capture list
C. It prints nil because self is not captured
D. It causes a retain cycle because closure captures self strongly

Solution

  1. Step 1: Analyze closure capturing self

    The closure assigned to the property captures self strongly by default.
  2. Step 2: Understand retain cycle effect

    Since self holds closure and closure holds self, neither is released, causing a retain cycle and memory leak.
  3. Final Answer:

    It causes a retain cycle because closure captures self strongly -> Option D
  4. Quick Check:

    Strong capture causes retain cycle = A [OK]
Hint: Closures capturing self strongly cause retain cycles unless weak/unowned used [OK]
Common Mistakes:
  • Assuming no retain cycle without capture list
  • Thinking it causes syntax error
  • Believing closure prints nil
4. Given this code, what is the best fix to avoid the retain cycle?
class ViewController {
    var completion: (() -> Void)?
    func configure() {
        completion = {
            print(self)
        }
    }
}
medium
A. Call configure() twice
B. Use [weak self] in the closure capture list
C. Declare completion as a strong reference
D. Remove the closure property entirely

Solution

  1. Step 1: Identify cause of retain cycle

    The closure captures self strongly, causing retain cycle with the property.
  2. Step 2: Apply weak capture to break cycle

    Adding [weak self] in the capture list makes self a weak reference, breaking the cycle.
  3. Final Answer:

    Use [weak self] in the closure capture list -> Option B
  4. Quick Check:

    Weak capture breaks retain cycle = D [OK]
Hint: Add [weak self] to closure capture list to fix retain cycles [OK]
Common Mistakes:
  • Removing closure property instead of fixing capture
  • Making completion strong reference (already strong)
  • Calling configure multiple times doesn't fix cycle
5. You have a class with a closure property that captures self unowned:
class Worker {
    var task: (() -> Void)?
    func start() {
        task = { [unowned self] in
            print("Working with \(self)")
        }
    }
}
var w: Worker? = Worker()
w?.start()
w = nil
What happens when w is set to nil and why?
hard
A. Worker instance is deallocated safely; unowned does not retain self
B. Runtime crash occurs because closure accesses deallocated self
C. Memory leak occurs due to strong capture
D. Nothing happens; closure is never called

Solution

  1. Step 1: Understand unowned capture behavior

    Unowned references do not increase retain count but expect self to exist while closure runs.
  2. Step 2: Analyze deallocation when w = nil

    Setting w to nil releases Worker instance; no retain cycle exists because closure holds unowned self.
  3. Final Answer:

    Worker instance is deallocated safely; unowned does not retain self -> Option A
  4. Quick Check:

    Unowned capture allows safe deallocation = A [OK]
Hint: Unowned captures don't retain but expect self alive during use [OK]
Common Mistakes:
  • Confusing unowned with weak causing runtime crash
  • Assuming strong capture causes leak here
  • Thinking closure runs automatically on deallocation
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard