I have a retained cycle so my viewcontroller's deinit won't be called, and I'm trying to resolve this my adding [unowned self], but I'm not too sure where to put the unowned in my cases:
Case 1
class YADetailiViewController: UIViewController {
var subscription: Subscription<YAEvent>?
override func viewDidLoad() {
super.viewDidLoad()
if let query = self.event.subscribeQuery() {
self.subscription = Client.shared.subscribe(query)
self.subscription?.handle(Event.updated) {
query, object in
DispatchQueue.main.async {
[unowned self] in// Put unowned here won't break the cycle, but it does compile and run
self.pageViewLabel.text = String(object.pageViews) + " VIEW" + ((object.pageViews > 1) ? "S" : "")
}
}
}
}
}
Case 2
override func viewDidLoad() {
super.viewDidLoad()
if let query = self.event.subscribeQuery() {
self.subscription = Client.shared.subscribe(query)
self.subscription?.handle(Event.updated) {
[unowned self] query, object in // Put unowned breaks the cycle, and deinit is called
DispatchQueue.main.async {
self.pageViewLabel.text = String(object.pageViews) + " VIEW" + ((object.pageViews > 1) ? "S" : "")
}
}
}
}
I'm curious what's the differences between these two scenarios and why one works but not the other
Indeed, as correctly mentioned by @matt the problem is related to the time when
self
is captured. In fact int this codeself
is captured twice:handle
methodasync
method (during thehandle
closure execution)The outer closure needs
self
to pass it to the inner closure, otherwise the inner closure won't be able to capture it.The nature or retain cycle is the following:
self
(YADetailiViewController) ->subscription
-> closure (handle
parameter) ->self
. To break this cycle it's enough to not retainself
in that (outer) closure. That's why the second code sample works.The second capture of
self
(in the inner closure) happens only when the outer closure is executed and lasts until thatasync
block is executed - usually it's quite a short time.In the first code sample you're not breaking the cycle. The second capture of
self
doesn't happen but the first one (causing thy cycle) is still present.And if your
handle
closure can still be called when the view controller is already deinited/released then as suggested by @AdrianBobrowski you should useweak
instead ofunowned
to prevent possible crash.