ios – Can we all the time use weak self and all the time want to make use of guard let self inside any closure?

0
16
ios – Can we all the time use weak self and all the time want to make use of guard let self inside any closure?


last class ViewController {
    non-public func bind() {
        self.viewModel.loadData { [weak self] in
            guard let self else { return } 
            self.doSomething()
        }
    }
}

First, you might be appropriate that in lots of instances the [weak self] right here is just not actually vital, however not for the explanation you give:

I really feel it isn’t neccessary, as a result of guard let self present a robust reference inside a block to a weak reference outdoors block, it ensures that if block captures the weak self earlier than deallocate, the code inside block nonetheless can execute.

That isn’t what is going on. This closure doesn’t itself maintain a robust reference to self, so self is free to deallocate and when this closure is executed, the guard let self will fail and return.

The explanation it’s usually not wanted is that loadData, if written appropriately, probably doesn’t create a reference cycle. The explanation for weak references is to interrupt reference cycles the place viewModel holds onto self through the reference, and self holds onto viewModel. If that’s true, then self won’t ever be launched and can leak.

However a technique like loadData ought to typically “do async factor, name completion handler, and launch completion handler.” And if it does that, then there’s solely a reference cycle all through loadData, and that typically is fascinating.

However there are numerous instances the place everlasting cycles do exist and have to be damaged with a weak reference, so this sample is extraordinarily widespread.

The order of operation seems to be like this:

    self.viewModel.loadData { [weak self] in ... }

A closure is created which holds a weak reference to self. That closure is handed to loadData().

We have no idea what loadData() does with that closure. However it’s a good guess that in some unspecified time in the future sooner or later it calls it, most likely a single time. And when it does, the following operation is:

        guard let self else { return } 

That is syntactic sugar for:

        guard let `self` = self else { return } 

This checks if the weak reference to self is nil. In that case, then it returns. If not, it creates a brand new (shadow) variable referred to as self that may be a robust reference.

So long as this shadow self robust reference is in scope, the ViewController can’t deallocate.

On this particular case, utilizing the guard is an identical to utilizing self?.doSomething(). However the guard scales a lot better and is mostly most well-liked. It is positively most well-liked if the closure is a couple of line. Think about this:

    self.viewModel.loadData { [weak self] in
        self?.doSomething()
        print("RUNNING")
        self?.doSomethingElse()
    }

This does the next:

  1. If self at present exists, take a robust reference and name doSomething().
  2. Drop the robust reference to self.
  3. Print “RUNNING” (whether or not self exists or not)
  4. If self at present exists, take a robust reference and name doSomethingElse().
  5. Drop the robust reference to self.

Which means that between steps 2 and 4, self can deallocate, which means that some operations might run and a few might not. That is very not often what you need. You both need the entire block to run or nothing. The guard let self else { return } incantation is a handy and idiomatic option to categorical that.

So coming again to your query about DispatchQueue.principal.async, I can’t consider any case the place these ought to seize weak self. Individuals apply it on a regular basis, and IMO it is a mistake (although not a horrible one).

When DispatchQueue.async() known as on a world queue (together with principal), I anticipate the supplied block to all the time be executed, and the goal needs to be preserved lengthy sufficient to make sure that occurs. IMO including additional equipment to permit the goal to vanish in that fraction of a second is each wasteful and complicated. Nonetheless, additionally it is quite common and doesn’t do lots of hurt.

There’s an obscure case the place scheduling a block that holds a weak reference on a DispatchQueue is smart. If in case you have a customized queue that you simply droop(), you might have considered trying objects to deallocate earlier than you resume() the queue. The variety of iOS builders who handle queues this manner rounds to zero; I solely carry it up for completeness. (I feel the final time I referred to as droop() on a queue was earlier than Swift got here out.)

LEAVE A REPLY

Please enter your comment!
Please enter your name here