
Closures are used heavily in Swift APIs for callbacks, collection operations, and asynchronous work. Interviewers ask this to test whether you understand closure lifetime, compiler rules, and memory implications.
Explain the difference between escaping and non-escaping closures in Swift.
Your answer should address:
@escaping is required in some function parametersselfThe expected depth is practical language understanding rather than compiler internals. You should define both terms clearly, explain when the compiler requires @escaping, and mention the relationship to asynchronous execution, stored callbacks, and retain-cycle risk.
A non-escaping closure is guaranteed to be executed before the function it is passed into returns. Because it does not outlive the function call, Swift can apply stricter safety rules and simpler capture behavior.
func process(_ action: () -> Void) {
action()
}
An escaping closure may be stored, returned, or executed after the surrounding function has returned. Swift requires the @escaping annotation on closure parameters in these cases so the lifetime extension is explicit.
func fetchData(completion: @escaping (String) -> Void) {
DispatchQueue.global().async {
completion("done")
}
}
Escaping closures can keep referenced objects alive longer, so Swift requires explicit self when accessing instance members inside them. This makes capture semantics visible and helps developers think about retain cycles.
class ViewModel {
var value = 0
func start() {
runLater { [weak self] in
self?.value += 1
}
}
}
The most common escaping cases are asynchronous APIs and callback storage. If a closure is saved in a property, appended to an array, or dispatched for later execution, it escapes.
var handlers: [() -> Void] = []
func addHandler(_ handler: @escaping () -> Void) {
handlers.append(handler)
}
Non-escaping closures are easier for the compiler to optimize because their lifetime is limited to the function body. They also reduce accidental memory retention compared with escaping closures.