Skip to main content

How would you avoid retain cycles in a closure?

· 2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

This will build off our understanding of ARC from the previous questions.

Often times, closures will introduce retain cycles. Since a closure is a reference type it maintains a strong reference to all of the objects referenced in the body of the closure thereby increasing their retain count.

To manage this, we can use a capture list. This allows us to explicitly specify which objects we want to maintain a reference to, but more importantly whether we want those references to be weak, strong, or unowned.

We can pick weak or unowned(where applicable) to ensure that we can still reference all of the objects the closure needs, but we don’t inadvertently increase their retain count and introduce a retain cycle.

The following example has a retain cycle as the body of the closure creates a strong reference to isUserActive and isUserOnlineView.

class RetainCycleDemo {
@IBOutlet var isUserOnlineView: UIView!

var isUserActive = false

func setUserActivityStatusView() {
userService.checkUserOnlineStatus { isOnline in
self.isUserActive = isOnline

if isOnline {
self.isUserOnlineView.backgroundColor = .green
} else {
self.isUserOnlineView.backgroundColor = .red
}
}
}
}

Fortunately, we can fix this by simply using a weak reference to self instead. And, voila - no more retain cycles!

class RetainCycleDemo {
@IBOutlet var isUserOnlineView: UIView!
var isUserActive = false

func setUserActivityStatusView() {
userService.checkUserOnlineStatus { [weak self] isOnline in
self?.isUserActive = isOnline
if isOnline {
self?.isUserOnlineView.backgroundColor = .green
} else {
self?.isUserOnlineView.backgroundColor = .red
}
}
}
}