How to Create a Reference Cycle Using Dispatchqueues

How can I create a reference cycle using dispatchQueues?

You say:

From what I understand the setup here is:

self ---> queue
self <--- block

The queue is merely a shell/wrapper for the block. Which is why even if I nil the queue, the block will continue its execution. They’re independent.

The fact that self happens to have a strong reference to the queue is inconsequential. A better way of thinking about it is that a GCD, itself, keeps a reference to all dispatch queues on which there is anything queued. (It’s analogous to a custom URLSession instance that won’t be deallocated until all tasks on that session are done.)

So, GCD keeps reference to the queue with dispatched tasks. The queue keeps a strong reference to the dispatched blocks/items. The queued block keeps a strong reference to any reference types they capture. When the dispatched task finishes, it resolves any strong references to any captured reference types and is removed from the queue (unless you keep your own reference to it elsewhere.), generally thereby resolving any strong reference cycles.


Setting that aside, where the absence of [weak self] can get you into trouble is where GCD keeps a reference to the block for some reason, such as dispatch sources. The classic example is the repeating timer:

class Ticker {
private var timer: DispatchSourceTimer?

func startTicker() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".ticker")
timer = DispatchSource.makeTimerSource(queue: queue)
timer!.schedule(deadline: .now(), repeating: 1)
timer!.setEventHandler { // whoops; missing `[weak self]`
self.tick()
}
timer!.resume()
}

func tick() { ... }
}

Even if the view controller in which I started the above timer is dismissed, GCD keeps firing this timer and Ticker won’t be released. As the “Debug Memory Graph” feature shows, the block, created in the startTicker routine, is keeping a persistent strong reference to the Ticker object:

repeating timer memory graph

This is obviously resolved if I use [weak self] in that block used as the event handler for the timer scheduled on that dispatch queue.

Other scenarios include a slow (or indefinite length) dispatched task, where you want to cancel it (e.g., in the deinit):

class Calculator {
private var item: DispatchWorkItem!

deinit {
item?.cancel()
item = nil
}

func startCalculation() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".calcs")
item = DispatchWorkItem { // whoops; missing `[weak self]`
while true {
if self.item?.isCancelled ?? true { break }
self.calculateNextDataPoint()
}
self.item = nil
}
queue.async(execute: item)
}

func calculateNextDataPoint() {
// some intense calculation here
}
}

dispatch work item memory graph

All of that having been said, in the vast majority of GCD use-cases, the choice of [weak self] is not one of strong reference cycles, but rather merely whether we mind if strong reference to self persists until the task is done or not.

  • If we’re just going to update the the UI when the task is done, there’s no need to keep the view controller and its views in the hierarchy waiting some UI update if the view controller has been dismissed.

  • If we need to update the data store when the task is done, then we definitely don’t want to use [weak self] if we want to make sure that update happens.

  • Frequently, the dispatched tasks aren’t consequential enough to worry about the lifespan of self. For example, you might have a URLSession completion handler dispatch UI update back to the main queue when the request is done. Sure, we theoretically would want [weak self] (as there’s no reason to keep the view hierarchy around for a view controller that’s been dismissed), but then again that adds noise to our code, often with little material benefit.


Unrelated, but playgrounds are a horrible place to test memory behavior because they have their own idiosyncrasies. It’s much better to do it in an actual app. Plus, in an actual app, you then have the “Debug Memory Graph” feature where you can see the actual strong references. See https://stackoverflow.com/a/30993476/1271826.

Retain cycle with strong reference to dispatch_queue

It is merely a strong reference to self that is eliminated automatically when the block finishes running and GCD releases the block. Note, this is a strong reference between the queue object itself, the block, and self, but not to databaseQueue. E.g. even if databaseQueue was some local reference that had fallen out of scope after you dispatched but before it ran, you'd still have a strong reference between the queue object, the block, and self.

If you don't want that strong reference at all, use weakSelf pattern:

typeof(self) __weak weakSelf = self;
dispatch_async(self.databaseQueue, ^{
[weakSelf.dao deleteRetries];
});

You asked:

Please could you elaborate more on "Note, this is a strong reference between the queue object itself, the block, and self, but not to databaseQueue"?

Consider:

- (void)runManyTasks {
dispatch_queue_t queue = dispatch_queue_create("com.domain.app.foo", 0);

for (NSInteger i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self doSomething];
});
}
}

- (void)doSomething {
[NSThread sleepForTimeInterval:1];
}

Even though I have no references in my code to that local variable, queue, after runManyTasks finishes, if I call runManyTasks, GCD will keep its own strong reference to the actual underlying queue object until all tasks finish, and the queue will keep copies of those blocks until they finish running, and those blocks will maintain a strong reference to self until GCD finishes with all of them (roughly 10 seconds, in this example).


You go on to edit your question and ask:

the current class holds a strong reference to viewControllerToDismiss and there is a code which looks like:

[viewControllerToDismiss dismissViewControllerAnimated:shouldAnimateDismiss completion:^{
[self performSomeAction];
}

is this a retain cycle?

For all practical considerations, no. That block is released as soon as the dismissal animation is done, so you generally wouldn't complicate your code with weakSelf pattern here. In fact, that view controller isn't dismissed until the animation finishes, anyway, so there's absolutely nothing gained from weakSelf pattern (other than making code more convoluted).

Avoiding using self in the DispatchQueue

Don't worry! DispatchQueue closures don't cause retain cycles.

Where does the weak self go?

First of all, note that you generally don't need to worry about retain cycles with DispatchQueue.main.asyncAfter, as the closure will be executed at some point. Therefore whether or not you weakly capture self, you won't be creating a permanent retain cycle (assuming that tickle.fresh also doesn't).

Whether or not you put a [weak self] capture list on the outer asyncAfter closure depends entirely on whether you want self to be retained until the closure is called (after the time you set). If you don't need self to remain alive until the closure is called, put [weak self] in, if you do, then don't put it in.

Whether or not you put a [weak self] on the inner closure (the one passed to tickle.fresh) depends on whether you've already weakly captured self in the outer closure. If you haven't, then you can put [weak self] in order to prevent the inner closure from retaining it. If however, the outer closure has already weakly captured self, then the inner closure will already have a weak reference to self, thus adding [weak self] to the inner closure will have no effect.

So, to summarise:


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
tickle.fresh { msg in
self.paint()
}
}

self will be retained by both the outer and inner closure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
tickle.fresh { msg in
self?.paint()
}
}

self will not be retained by either closure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
tickle.fresh { [weak self] msg in
self?.paint()
}
}

Same as the above, the additional [weak self] for the inner closure has no effect, as self is already weakly captured by the outer closure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
tickle.fresh { [weak self] msg in
self?.paint()
}
}

self will be retained by the outer closure, but not the inner closure.


Of course, it might be that you don't want self to be retained by the outer closure, but you do want it to be retained by the inner closure. In such cases, you can declare a local variable in the outer closure in order to hold a strong reference to self, when you can then capture in the inner closure:

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
guard let strongSelf = self else { return }
tickle.fresh { msg in
strongSelf.paint()
}
}

Now, self won't be kept alive by the outer closure, but once it's called, if self still exists, it will be kept alive by the inner closure until that closure has been deallocated.


In response to:

Is a strong reference to a weak reference, a weak or strong reference?

Weak references are implemented as optionals, which are value types. Therefore you cannot directly have a strong reference to one – instead you first have to unwrap it, and then take a strong reference to the underlying instance. In this case you're simply dealing with a strong reference (exactly like my example above with strongSelf).

However, if a weak reference is boxed (this happens with closure capture – the value type will be put into a heap-allocated box) – then you can indeed have a strong reference to that box. The effect of this is equivalent to a weak reference to the original instance, you just have an invisible bit of extra indirection.

In fact, this is exactly what happens in the example where the outer closure weakly captures self and the inner closure 'strongly captures' that weak reference. The effect is that neither closure retains self.

What happens to Dispatch Queues when UIViewController is Deallocated?

Is the [unowned self] self here necessary?

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 use unowned if you know the block can never be called after self 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 and unowned 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 reference self, but without a weak/unowned qualifier.

My question is what happens to DispatchQueues when a view controller is deallocated?

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 to self (i.e. the view controller), they will continue to run after the view controller is released. This is why it's critical to not use unowned in this context.


For what it's worth, empirical tests can be illuminating. Consider:

class SecondViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let queue = DispatchQueue(label: "com.domain.app.SecondViewController")

for i in 0 ..< 10 {
queue.async { [weak self] in
print("closure \(i) start")
self?.performSomeTask(i)
print("closure \(i) finish")
}
}
}

private func performSomeTask(_ value: Int) {
print("performSomeTask starting \(value)")
Thread.sleep(forTimeInterval: 5) // you wouldn't generally `sleep`, but merely for diagnostic purposes
print("performSomeTask finishing \(value)")
}

deinit {
print("deinit SecondViewController")
}

}

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], the performSomeTask won't run after the view controller is dismissed.

  • If you replace weak with unowned (and obviously remove the ? in self?.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 to self, you'll see it won't deallocate the view controller until all queued blocks finish.

How to prevent memory leak with using self in closure

First, why are you getting that assertion failure? Because you’re letting FileDownloader instance fall out of scope. You haven’t shared how you’re invoking this, but you’re likely using it as a local variable. If you fix that, your problem goes away.

Second, when you changed your implementation to remove the [weak self] pattern, you don’t have a strong reference cycle, but rather you’ve just instructed it to not release the FileDownloader until the download is done. If that’s the behavior you want, then that’s fine. It’s a perfectly acceptable pattern to say “have this keep a reference to itself until the asynchronous task is done.” In fact, that’s precisely what URLSessionTask does. Clearly, you need to be absolutely clear regarding the implications of omitting the [weak self] pattern, as in some cases it can introduce a strong reference cycle, but not in this case.


Strong reference cycles only occur when you have two objects with persistent strong references to each other (or sometimes more than two objects can be involved). In the case of URLSession, when the download is done, Apple prudently wrote downloadTask method so that it explicitly releases the closure after calling it, resolving any potential strong reference cycle.

For example, consider this example:

class Foo {
func performAfterFiveSeconds(block: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.doSomething()

block()
}
}

func doSomething() { ... }
}

The above is fine because asyncAfter releases the closure when it’s run. But consider this example where we save the closure in our own ivar:

class BarBad {
private var handler: (() -> Void)?

func performAfterFiveSeconds(block: @escaping () -> Void) {
handler = block

DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.calledWhenDone()
}
}

func calledWhenDone() {
// do some stuff

doSomething()

// when done, call handler

handler?()
}

func doSomething() { ... }
}

Now this is a potential problem, because this time we save the closure in an ivar, creating a strong reference to the closure, and introducing risk of a classic strong reference cycle.

But fortunately this is easily remedied:

class BarGood {
private var handler: (() -> Void)?

func performAfterFiveSeconds(block: @escaping () -> Void) {
handler = block

DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.calledWhenDone()
}
}

func calledWhenDone() {
// do some stuff

doSomething()

// when done, call handler

handler?()

// make sure to release handler when done with it to prevent strong reference cycle

handler = nil
}

func doSomething() { ... }
}

This resolves the strong reference cycle when it sets handler to nil. This is effectively what URLSession (and GCD methods like async or asyncAfter) do. They save the closure until they call it, and then they release it.

Swift [weak self] for Dispatching on main in a nested closure

You're way overthinking this. The need for weak self has nothing to do with what thread you're going to be on, and it has nothing to do with dispatch queues. If you're going to be worrying about weak self every time you see a pair of curly braces, you'll be a nervous wreck. /p>

Weak self has to do with whether there will be a retain cycle. Retain cycles are caused through long term storage of a closure by an object it captures.

Nothing like that is going on here. When you are just saying "perform this action now", regardless of the thread, no retain cycle arises. So no precautions are necessary.

Is it required to to use weak reference's within a singleton class?

In case of DispatchQueue closures don't add any capture list at all, nowhere.

DispatchQueue closures don't cause retain cycles because self doesn't own them.

Basically capture lists inside a singleton object are not needed as the singleton is never going to be deallocated.



Related Topics



Leave a reply



Submit