I am trying to better understand retain cycles, especially relative to Dispatch Queues. I am working with AVFoundation and managing an AVCaptureSession on a sessionQueue:
private let sessionQueue = DispatchQueue(label: "com.andrewferrarone.sessionQueue")
in a lot of code examples in the apple documentation I see this:
self.sessionQueue.async { [unowned self]
//
}
Is the [unowned self]
self here necessary? self (the viewController) references self.sessionQueue
and the closure dispatched to self.sessionQueue
captures self. Is this a reference cycle? self is not referencing the closure, just the DispatchQueue. If [unowned self]
is necessary, then from what I understand, I only want to use unowned self
if I am certain that self will not be nil. So lets say I put a task on sessionQueue
that takes a long time and the viewController gets popped off and is deallocated before the task finishes? What happens to sessionQueue
and the task? If its still around then, when it tries to access self, the app will crash. On the other hand, since unowned self doesn't increment the retain count of self, then it won't prevent the viewController from being deallocated.
So My question is what happens to DispatchQueues when a viewController is deallocated and what happens in this case, if a viewController gets deallocated before a dispatchQueue task is finished? If someone could shed some light on what all is going on here that would be very helpful and appreciated.
Thanks for the help my friends!
Not only is the use of
[unowned self]
not necessary, but it's very dangerous in an asynchronously dispatched block. You end up with a dangling pointer to a deallocated object.If you don't want to keep keep a strong reference to
self
in an asynchronous call, use[weak self]
, instead. You should only useunowned
if you know the block can never be called afterself
is deallocated. Obviously, with async call, you don't know this, so[unowned self]
should not be used in that context.Whether you use
[weak self]
or use strong references is a question of whether you need the asynchronously executed block to keep a strong reference to the object in question or not. For example, if you're updating a view controller's view's controls only, then[weak self]
is fine (no point in updating a view that has been dismissed).The more critical use of
weak
andunowned
references is to avoid strong reference cycles. But that doesn't apply in the example you've provided. You only need to worry about those cycles if the view controller keeps some reference to the blocks itself (e.g. you have some closure property) and those closures referenceself
, but without aweak
/unowned
qualifier.Those queues will continue to exist, as will any dispatched blocks, until (a) all dispatched blocks finish; and (b) there are no more strong references to the queue.
So if you asynchronously dispatch blocks with
weak
references toself
(i.e. the view controller), they will continue to run after the view controller is released. This is why it's critical to not useunowned
in this context.For what it's worth, empirical tests can be illuminating. Consider:
If you dismiss this view controller while the dispatched blocks are queued up and running, you'll see:
With
[weak self]
, the view controller is retained only until the current dispatched block finishes, the view controller will then be released, and the rest of the blocks will rapidly fire off, but because of[weak self]
, theperformSomeTask
won't run after the view controller is dismissed.If you replace
weak
withunowned
(and obviously remove the?
inself?.performSomeTask(...)
), you'll see it crash if you dismiss the view controller before the queued blocks have had a chance to start. This is illustrative of why[unowned self]
is so dangerous with asynchronous code.If you simply remove
[weak self]
altogether and let it use a implicitly strong reference toself
, you'll see it won't deallocate the view controller until all queued blocks finish.