Where Does the Weak Self Go

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 is difference between [self] in & [weak self] in in Swift?

Writing [self] is same as leaving the closure as it is meaning it could cause retain cycle unlike [weak self] , BTW no need for any of them with DispatchQueue.main.async { as GCD queues don't hold a strong reference to self

In Swift, if I have a closure capturing [weak self], is it good practice to unwrap the optional self at the beginning of the closure?

I believe that the first implementation is better because self could become nil between the statements

And that's why the second implementation is in fact better! If self is not nil at the first statement, the first statement makes it so that self couldn't become nil between the statements. It retains self exactly for the remainder of the block. This is called "the weak–strong dance".

guard let self = self else { return }
// ^^^^ this self is _strong_, not weak; it retains self

Is it the right way using `[weak self]` in swift closure?

Your pattern has race condition. If self was deallocated at the exact same time as your completion handler closure was executing, it could crash. As a general rule, avoid using the ! forced unwrapping operator if you can.

  1. I’d lean towards the guard “early exit” pattern (reducing nested braces, making code easier to read). The standard Swift 4.2 solution is:

    someTask { [weak self] result in
    guard let self = self else { return }

    self.xxx = yyy
    self.doLongTermWork()
    self.finish()
    }
  2. Before Swift 4.2, which implemented SE-0079, we would have to do something like:

    someTask { [weak self] result in
    guard let strongSelf = self else { return }

    strongSelf.xxx = yyy
    strongSelf.doLongTermWork()
    strongSelf.finish()
    }

    You can see why we prefer the Swift 4.2 improvement, as this strongSelf syntax is inelegant.

  3. The other obvious alternative is just:

    someTask { [weak self] result in
    self?.xxx = yyy
    self?.doLongTermWork()
    self?.finish()
    }

    Sometimes you need the “weak self - strong self dance” (the first two alternatives), but it would not appear to be the case here. This is likely sufficient.

There are other scenarios/edge cases that one might contemplate, but these are the basic approaches.

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.

Weak self in closures and consequences example

The question should not be "can I use weak reference," but rather "should I use weak reference." You use weak references to avoid strong reference cycles or to keep a closure from hanging on to something after it may have been disposed. But don't just add weak references because you can.

  1. In case 1, you probably do want to use [weak self]. Why? Because if the view controller was dismissed while the authorization was underway, do you really want to keep a reference to a view controller that was dismissed? Probably not in this case.

  2. In case 2, you theoretically could use [weak self] in the animation block, but why would you? There's no reason to. The weak reference is something you do with completion handlers and/or closure variables, but for an animation block it offers no utility, so I wouldn't do it there. To use weak here suggests a misunderstanding of the memory semantics involved.

  3. In case 3, you have two separate issues.

    • In the didSelectItemAtIndexHandler, that probably should use [unowned self] because the object's own closure is referring to itself.

      It may be a moot issue, as I don't see you actually using that BTNavigationDropdownMenu (perhaps that initializer is adding itself to the navigation controller, but that's not a well designed initializer if so, IMHO).

      But as a general concept, when an object has a handler closure that can only be called when the object is still around, but shouldn't, itself, cause the object to be retained, you'd use [unowned self].

    • In the broader checkUserLoggedIn closure, the question is whether that's a completion handler. If so, you probably should use [weak self], because this could be initiated and be running by the time self is dismissed, and you don't want to have checkUserLoggedIn keep a reference to a view controller that was dismissed. But you wouldn't want to use [unowned self] because that would leave you with dangling pointers if it had been released by the time the closure runs.

      As an aside, you contemplate:

      weak var weakSelf = self 

      That is a bit unswifty. You would use the [weak self] pattern at the start of the checkUserLoggedIn closure. If you have an example where you're tempted to use weak var weakSelf = ..., you should edit your question, including an example of where you want to use that pattern. But this is not one of those cases.

async/await, Task and [weak self]

Bottom line, there is often little point in using [weak self] capture lists with Task objects. Use cancelation patterns instead.


A few detailed considerations:

  1. Weak capture lists are not required.

    You said:

    in traditional concurrency in Swift, if you are performing (for example) a network request inside a class, and in the completion of that request you reference a function that belongs to that class, you must pass [weak self]

    This is not true. Yes, it may be prudent or advisable to use the [weak self] capture list, but it is not required. The only time you “must” use a weak reference to self is when there is a persistent strong reference cycle.

    For well-written asynchronous patterns (where the called routine releases the closure as soon as it is done with it), there is no persistent strong reference cycle risk. The [weak self] is not required.

  2. Nonetheless, weak capture lists are useful.

    Using [weak self] in these traditional escaping closure patterns still has utility. Specifically, in the absence of the weak reference to self, the closure will keep a strong reference to self until the asynchronous process finishes.

    A common example is when you initiate a network request to show some information in a scene. If you dismiss the scene while some asynchronous network request is in progress, there is no point in keeping the view controller in memory, waiting for a network request that merely updates the associated views that are long gone.

    Needless to say, the weak reference to self is really only part of the solution. If there’s no point in retaining self to wait for the result of the asynchronous call, there is often no point in having the asynchronous call continue, either. E.g., we might marry a weak reference to self with a deinit that cancels the pending asynchronous process.

  3. Weak capture lists are less useful in Swift concurrency.

    Consider this permutation of your function2:

    func function2() {
    Task { [weak self] in
    let result = await apiClient.performNetworkRequestAsync()
    self?.printSomething()
    }
    }

    This looks like it should not keep a strong reference to self while performNetworkRequestAsync is in progress. But the reference to a property, apiClient, will introduce a strong reference, without any warning or error message. E.g., below, I let AsyncClass fall out of scope at the red signpost, but despite the [weak self] capture list, it was not released until the asynchronous process finished:

    Sample Image

    The [weak self] capture list accomplishes very little in this case. Remember that in Swift concurrency there is a lot going on behind the scenes (e.g., code after the “suspension point” is a “continuation”, etc.). It is not the same as a simple GCD dispatch. See Swift concurrency: Behind the scenes.

    If, however, you make all property references weak, too, then it will work as expected:

    func function2() {
    Task { [weak self] in
    let result = await self?.apiClient.performNetworkRequestAsync()
    self?.printSomething()
    }
    }

    Hopefully, future compiler versions will warn us of this hidden strong reference to self.

  4. Make tasks cancelable.

    Rather than worrying about whether you should use weak reference to self, one could consider simply supporting cancelation:

    var task: Task<Void, Never>?

    func function2() {
    task = Task {
    let result = await apiClient.performNetworkRequestAsync()
    printSomething()
    task = nil
    }
    }

    And then,

    @IBAction func didTapDismiss(_ sender: Any) {
    task?.cancel()
    dismiss(animated: true)
    }

    Now, obviously, that assumes that your task supports cancelation. Most of the Apple async API does. (But if you have written your own withUnsafeContinuation-style implementation, then you will want to periodically check Task.isCancelled or wrap your call in a withTaskCancellationHandler or other similar mechanism to add cancelation support. But this is beyond the scope of this question.)

Avoiding using self in the DispatchQueue

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



Related Topics



Leave a reply



Submit