53
    UIView.animateWithDuration(1,
        animations: { [unowned self] in
            self.box.center = self.boxTopRightPosition
        },
        completion: { [unowned self] completed in
            self.box.hidden = true
    })

Is it necessary to avoid memory leak?

mfaani
  • 33,269
  • 19
  • 164
  • 293
WildCat
  • 1,961
  • 6
  • 24
  • 35
  • 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

7 Answers7

71

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.

Kirsteins
  • 27,065
  • 8
  • 76
  • 78
  • 1
    Is there any way to create closures like `animations` or `completion` here? – WildCat Nov 20 '14 at 12:54
  • Why haven't they use the noescape keyword? – Plot May 25 '16 at 09:10
  • 1
    @Plot UIKit is written in Objective-C and it is interpolated in Swift. Objective-C do not have `@noescape` feature. – Kirsteins May 25 '16 at 09:14
  • 7
    Is it documented somewhere that the block is therefore not retained by self? – Tieme Dec 02 '18 at 20:25
  • 3
    Aren't the the closures indirectly retained by self, if self retains a strong reference to the UIView? – Bradley Thomas Mar 10 '19 at 13:44
  • 1
    @Tieme Not documented. But see [here](https://stackoverflow.com/questions/56061092/how-can-i-create-a-reference-cycle-using-dispatchqueues). Make sure you fully understand the question, then read the answer. It should explain it very well – mfaani May 16 '19 at 15:35
  • @BradThomas Every closure, pointing to `self`, will retain `self`, but what's key is if `self` is also retaining the closure or not. `self` does NOT retain dispatchQueues. But it does retain it closure properties, or closures on its properties, because its retaining the property. See [here](https://stackoverflow.com/questions/56061092/how-can-i-create-a-reference-cycle-using-dispatchqueues). Make sure you fully understand the question, then read the answer. – mfaani May 16 '19 at 15:37
  • Then who retain them ? I think they retained by self itself but it's all about how long they retained. In this case, it is definitive and will be released at one point so may be that why it's not creating a memory leak. – Abhijith May 22 '19 at 15:15
  • Wouldn't there be an issue if the animations occurs for like an hours (due to some bug) and we are stuck figuring out why we view controller is still not yet released despite dismissing it already? – CyberMew Oct 18 '19 at 10:52
  • They are "not needed" in the sense of "you never need to worry about memory leaks because these days devices have lots of ram". :O – Fattie Feb 03 '21 at 10:57
  • @CyberMew No. Even if you have a 1 hour long animation, as soon as the VC gets dismissed, `completion` will be called with `finished = false` – Lord Zsolt Apr 05 '21 at 12:16
  • @Tieme 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:26
55

Well, "necessary" isn't the same as "recommended". If your question is if it's necessary then @Kirsteins' response is fine, however imagine the situation where you want to animate something in your view controller after some work, but your view controller has been released (because it is not in the view hierarchy anymore or any other reason). In this case, if you don't use [weak self], your view controller won't get released until finishing the animation because you are retaining it in the animation block, but does it make sense to keep it retained until animating something which is not in the view anymore?

So, in few words, you don need to use a weak reference to self when animating UIKit, however, you don't need to keep your view retained if it's released, because an animation with no view doesn't make sense, so using weak is a good option.

Pablo A.
  • 2,042
  • 1
  • 17
  • 27
  • 2
    Nice and correct. Don't understand why this answer is not on top :) – Nikolai Ischuk Jul 30 '18 at 10:56
  • 4
    Correct. Long story short, using `[weak self]` or `[unowned self]` in dispatchQueues/animation blocks are just **means of control flow**. It's not a memory concern. _Eventually_ they'll execute/finish and release the object (depending on the block it could be in 2 or 10 or 200 seconds). For more on this see [How can I create a reference cycle using dispatchQueues?](https://stackoverflow.com/questions/56061092/how-can-i-create-a-reference-cycle-using-dispatchqueues) – mfaani May 16 '19 at 15:31
  • 1
    Totally agree with you, I animated my button repeatedly and it causes to memory leak. I have solve it with weak reference. – Gürhan KODALAK Jul 31 '19 at 09:36
  • This is false, if you try to deallocate the ViewController without weak self, the animation completion will be called immediately with success = false and the VC will be deallocated immediately. – Xys Dec 07 '22 at 10:42
42

No it's not needed. As Kirsteins says:

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.

But lhmgrassi says:

As soon as it be deallocated, the deinitializer will be called and the completion will never be executed.

I don't think this is true. The completion block will always be called. And if you use a strong self your object won't be deallocated until the completion block is executed.

However, if you use a [weak self], your object is not (temporary) retained by the completion block and might be deallocated before the completion block is fired. The completion block is still fired but self is already nil.

If you use a [unowned self] in your completion handler, you object might also be deallocated before the completion handler is called, which could result in a crash!

I've made an example illustrating this.

[gif illustrating the issue

Full source can be found on Github

Tieme
  • 62,602
  • 20
  • 102
  • 156
  • Yeah, it results in a crash with `unowned self` since you are capturing self. You'll be safe with just using self, since the completion block will not be retained by self. – José Oct 16 '19 at 15:11
  • Yes, exactly. – Tieme Oct 18 '19 at 11:45
  • Or use`[weak self]` if you don't need the completion block to hold the memory - you could be doing an endless animation loop or a very long animation and you don't want it to keep holding onto the memory. – CyberMew Oct 21 '19 at 01:53
  • You don't need `weak self`, it's a global function and won't retain `self`. – José Oct 24 '19 at 05:45
  • 1
    oh that's a great example/animation clip. thank you! – Stotch Feb 24 '21 at 19:24
7

@Plabo, as @Kirsteins said, animations and completion are not retained by self, so even if you start a animation and for any reason your view controller has been released, it will deallocated instantaneously. So, you don't need captured 'self'. Consider the silly example bellow:

class ViewController: UIViewController {

    @IBOutlet weak var button : UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        print("viewDidLoad ViewController")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        UIView.animate(withDuration: 20, animations: {
            self.button.frame = CGRect(x: 0, y: 300, width: 30, height: 30)
        }) { finished in
            self.button.frame = CGRect(x: 0, y: 100, width: 30, height: 30)
        }
    }

    deinit {
        print("deinit ViewController")
    }

}

As soon as it be deallocated, the deinitializer will be called and the completion will never be executed.

lhmgrassi
  • 131
  • 2
  • 6
5

Just the opposite. You want self to continue to exist long enough for the completion block to be called. Therefore having self be strong and retained through the escaping completion handler is a good thing.

The worry that usually leads people to use weak self is a retain cycle. But this isn't that. A retain cycle is when self retains the closure which retains self, causing a leak because now self can never be released. But this is not that situation at all. the closure, and therefore self, is being retained, but not by self! So there is some retaining going on, temporarily, but it is good, not bad.

matt
  • 515,959
  • 87
  • 875
  • 1,141
3

It is not needed to use weak/unowned in Animations, GCD or completion handler because :

The external object reference captured by them will only be kept for fixed time, which means it's definitive that it's going to be executed at one point of time. After this, it will be released so there is no chance of reference cycle which leads to memory leak.

As previous answers suggests,

If animations and completion are not retained by self, then who retains them ?

I didn't find any documentary evidence of this but what I believe is that they are retained by self itself but for a fixed amount of time. After this, the completion executes and release the self, which result the de-allocation of self.

In the retain cycle scenario, the closure is retained by self and self by closures for indefinite time, which is considered as reference cycle and memory leak.

Abhijith
  • 3,094
  • 1
  • 33
  • 36
1

Many wrong answers on this subject. weak self is never needed for animations.

As soon as you call dismiss on your ViewController, the animation completion will be called (with success at false), and your ViewController will be deallocated, even with a strong self reference.

With this test :

class TestVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .blue

        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.view.alpha = 1
            UIView.animate({
                self.view.alpha = 0
            }, duration: 40) { success in
                print("finished: \(success)")
                self.view.backgroundColor = .green
            }
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.dismiss(animated: true)
        }
    }

    deinit {
        print("deinit")
    }
}

(Notice the 40 second animation duration) In the logs, you will get after 6 seconds:

finished: false
deinit
Xys
  • 8,486
  • 2
  • 38
  • 56