An [unowned self] closure crashes after the screen is popped — fix it
This view controller stores a network client that calls back on completion. It captures [unowned self] to avoid a retain cycle. When the user pops the screen before the request finishes, the app crashes.
Constraints:
- Keep the closure; do not make the capture strong (that would leak).
- The fix must make a call after
selfis gone a safe no-op.
final class ProfileViewController: UIViewController {
let client = APIClient()
func load() {
client.fetchProfile { [unowned self] profile in
self.render(profile) // crashes after pop
}
}
func render(_ profile: Profile) { /* ... */ }
}
Find and fix the bug.
The closure outlives self, so its unowned reference points at freed memory and the call crashes — unowned wrongly assumed self would outlive it. Capture [weak self] with guard let self, so a dead self is a safe no-op.
- ✗Misreading the dangling-pointer crash as a retain cycle
- ✗Blaming the thread instead of the
unownedlifetime assumption - ✗Switching to a strong capture, which reintroduces the leak
- →When is
[unowned self]actually the correct choice for a closure? - →Why does
guard let selfmake a dead-selfcall a safe no-op?
unowned is not zeroed: it assumes the referent outlives the closure. Here the request finishes after the pop, self is freed, and the unowned access reads dead memory — hence EXC_BAD_ACCESS. Switch to [weak self] and unwrap with guard let self:
func load() {
client.fetchProfile { [weak self] profile in
guard let self else { return } // dead self -> safe early return
self.render(profile)
}
}
weak keeps the reference optional and zeroes it when self is freed, so after the pop the closure simply does nothing instead of crashing. A strong capture would also stop the crash but reintroduce the leak — the controller would live as long as the callback does.