9

After some research I found out my app was using way too much energy because of several UIView animations throughout the app, where I was capturing the related UIViewController in the completion block, without making a weak reference to it.

So actually, I changed this:

func animate() {
    UIView.animate(withDuration: 0.3, animations: {
        self.label.alpha = 0.5
    }) { _ in 
        self.animate()
    }
}

Into this :

func animate() {
    UIView.animate(withDuration: 0.3, animations: {
        self.label.alpha = 0.5
    }) { [weak self] _ in 
        self?.animate()
    }
}

However, I would like to know if I need to do the same with the animation block (the self.label.alpha = 0.5 one) ?

Thank you for your help

Fattie
  • 27,874
  • 70
  • 431
  • 719
Another Dude
  • 1,204
  • 4
  • 15
  • 33
  • 1
    The answers here are misleading. See [here](https://stackoverflow.com/questions/29770743/do-we-need-to-use-weak-self-inside-uianimationblocks-in-arc) – mfaani Sep 22 '21 at 16:25

4 Answers4

6

No, it is not needed in this case. animations and completion are not retained by self so there is no risk of strong retain cycle.

duplicated of Is it necessary to use [unowned self] in closures of UIView.animateWithDuration(...)?

Cruz
  • 2,602
  • 19
  • 29
5

You don't need to use [weak self] in UIView.animate(). You need to use weak when retain cycle is possible and animations block is not retained by self.

There is an article on medium about where [weak self] may and may not be needed

For more information:

There might be another issue for the energy problem.

Omer Faruk Ozturk
  • 1,722
  • 13
  • 25
  • It's not about `static`. It's about ownership. See [here](https://stackoverflow.com/questions/29770743/do-we-need-to-use-weak-self-inside-uianimationblocks-in-arc) – mfaani Sep 22 '21 at 16:27
0

What do you mean by:

my app was using way too much energy

I don't see any immediate relation between keeping a weak reference on your view controller and saving energy. Capturing self without increasing its retain count in your closure (that's what we mean by having a weak reference on an object) means that if no other strong reference on your view controller exists, then your view controller will be released sooner. Maybe you will save some RAM in a very short term.

I would suggest to investigate somewhere else to find where your "energy" issue comes from.

There are a lot of articles about the use of weak keyword.

https://medium.com/flawless-app-stories/you-dont-always-need-weak-self-a778bec505ef

Lucien
  • 11
  • 1
  • hi lucien - it's very obvious. because OP forgot about the weak self, he would be building up more and more of that view controller. there could be 100s of them. – Fattie Jun 16 '20 at 18:04
-5

Sure, you do need to use the "weak self" concept there.

In fact note: it is indeed very common in that situation that a VC is trashed during an animation ... so indeed, it's one of the places you "most need" to use the weak-self idea. It's a very common mistake in projects to forget that in an animation.

By the way, here's a handy syntax any time you need weak self:

func animate() {
    UIView.animate(withDuration: 0.3, animations: { [weak self] in
        guard let self = self else { return print("gotchya!") }
        self.label.alpha = 0.5
    }) { [weak self] in
        guard let self = self else { return print("gotchya!") }
        self.animate()
    }
}

Adding this line ..

        guard let self = self else { return }

.. can seem a bit long-winded, but, it means you then don't need the "?" after "self" every time you use it in long blocks.

Often you will have many uses of "self ..." inside such a block, and it seems more consistent to just use the "let self = self" idiom.

enter image description here

So even if there is only one line of code in the block (as in your two examples) it's a good way to be absolutely consistent everywhere and keep things simple.

Note that whenever you have a { return } you can just add a print statement so that you know what is going on during development ..

.... { [weak self] in
  guard let self = self else { return print("I avoided a crash, woot!") }

or

.... { [weak self] in
  guard let self = self else { return print("logical disaster near line 66") }

You do not have to do that, "{ return }" is fine. It's just for your convenience.

What the hell is this "self = self" business?

If you find the "self = self" idiom confusing ....... honestly, just don't worry about it. Always use that idiom. It's really just the "standard" thing you will see everywhere.

Always go for simple consistency in your code base!

More weak self mysteries ...

Here's a interesting QA from a handsome list member: Where does the weak self go?

And later there's this sort of confusion to worry about: What is the difference between a weak reference and an unowned reference?

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • Thank you very much for your help! – Another Dude Jun 16 '20 at 17:15
  • 2
    It's a bit confusing that this answer is the currently accepted one. To my understanding, `weak self` is not needed to prevent retain cycles in this case, which was the original questions. It can be used however as a means of control flow. – hoshy Feb 03 '21 at 07:55
  • I know what you mean @hoshy , as explained in my answer or here https://stackoverflow.com/a/39251222/294884 , you need it "in practice" because you never want to retain a disappeared view while an animatoin plays out. Put it this way - on any team you work on, if you forget it, the team lead will just add it to the commit :) It's something you "have to do". – Fattie Feb 03 '21 at 10:35
  • 1
    btw do you have any references to guidelines that confirms that `guard let self = self else { return }` is better than using `self?.` inside the closure? had an argue with a colleague during PR on that matter. My point was that it's better to leave the closure right away if there's no `self` anymore instead of executing random pieces of logic inside but I'd like to have some extra sauce :) – nrx Mar 17 '21 at 09:36
  • 12
    This answer is in fact **wrong**! Firstly, the `animations` closure will be ran immediately and will not be stored for the duration of the animation. I'm guessing `UIKit` will run it to determine the final state, then discard it. Secondly, the `completion` closure will be called **immediately** as the "VC gets trashed during animation", and the `finished` argument will be `false`. So neither closures need `[weak self]`. Additionally, if you wanted to use `[weak self]` in the `completion` to "not run the code if the VC got trashed", you can just use `!finished`. – Lord Zsolt Apr 05 '21 at 12:06
  • @nrx - your unrelated question is answered very easily (1) there is NO DIFFERENCE (2) I suggest in any language or platform, always use the common idiom, hence use that (3) note that it is much, much easier when you have many uses of "self" (4) definitely in a given project only use ONE of the two in the WHOLE project. cheers! – Fattie Apr 05 '21 at 16:01
  • @LordZsolt Hello Lord! :) sure, it is an often-discussed issue. I essentially agree with THIS answer: https://stackoverflow.com/a/39251222/294884 (Text begins "Well, "necessary" isn't the same as "recommended"...") – Fattie Apr 05 '21 at 16:02
  • @Fattie No, that answer is also wrong. Firstly, the `animations` closure runs synchronously and will not be retained, thus "your VC won't get released until finishing the animation" is a false => `[weak self]` is not "recommended". Secondly, when talking about the `completion` closure, yes there is a slight difference, if you use `[weak self]`, `self` will be deallocated ~1 run loop earlier, but that's negligible. If you have a 30s animation, and VC gets dismissed after 1s, `self` will not be retained for an extra 29s, because `completion` gets called+freed after the dismiss animation finishes – Lord Zsolt Apr 06 '21 at 21:09
  • And if you are doing some heavy work in the `completion` closure that you don't want to run anymore if the VC got dismissed, you should not use "`self` being deallocated" as the basis for this decision, but rather the `finished` argument. – Lord Zsolt Apr 06 '21 at 21:12
  • This is a wrong answer indeed. Take a look at this sample project: https://github.com/glennposadas/uiview-animate-weak-self – Glenn Posadas Aug 18 '22 at 08:14