0
0
Swiftprogramming~20 mins

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

Choose your learning style9 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.