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
selfis captured. In fact int this codeselfis captured twice:handlemethodasyncmethod (during thehandleclosure execution)The outer closure needs
selfto 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 (handleparameter) ->self. To break this cycle it's enough to not retainselfin 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 thatasyncblock is executed - usually it's quite a short time.In the first code sample you're not breaking the cycle. The second capture of
selfdoesn't happen but the first one (causing thy cycle) is still present.And if your
handleclosure can still be called when the view controller is already deinited/released then as suggested by @AdrianBobrowski you should useweakinstead ofunownedto prevent possible crash.