47

When writing the handler closure of a UIAlertAction, should the reference to self be strong (the default), weak, or unowned?

There have been posts relevant to this topic (1, 2, 3, 4) but I honestly don't see how they help in this case.

Let's focus on this typical code:

func tappedQuitButton() {
    let alert = UIAlertController(title: "Confirm quit", message: nil, preferredStyle: .ActionSheet)

    let quitAction = UIAlertAction(title: "Quit", style: .Default) { (action) in
        self.dismissViewControllerAnimated(true, completion: nil)
    }
    alert.addAction(quitAction)

    let cancelAction = UIAlertAction(title: "Cancel", style: .Default) { (action) in
        self.dismissViewControllerAnimated(true, completion: nil)
    }
    alert.addAction(cancelAction)

    presentViewController(alert, animated: true, completion: nil)
}

This is a function inside a UIViewController subclass, so self is the view controller presenting the alert.

The documentation says:

Use a weak reference to avoid reference cycles whenever it is possible for that reference to have “no value” at some point in its life. If the reference will always have a value, use an unowned reference instead.

I may be blind, but I still don't see how this helps answering my question about UIAlertAction.

In the above code, is it possible for self to be nil at some point in its life? Yes. So I should mark self as weak.

But then again, I can't think of a plausible scenario where self will be nil when the closure is called. So as far as that closure is concerned, self will always have a value. So I should mark self as unowned.

So, again, how should self be captured in a UIAlertAction's handler?

Community
  • 1
  • 1
Eric
  • 12,878
  • 13
  • 78
  • 119

1 Answers1

79

The key question to ask yourself is if your alert object is "owned" by self. In this case, it is not (because you declared let alert = ... in the function body). So you do not need to create this as a weak or unowned reference.

If alert was a property of self, then it would be "owned" by self and that is when you would want to create a weak reference to self in the closure "owned" by the alert.

Good Doug
  • 1,992
  • 14
  • 12
  • I have a tough time seeing this in terms of "ownership". Why aren't the local variables in an instance method "owned" by the instance calling it..? – Eric Jan 21 '16 at 22:43
  • 6
    The local variables are allocated on the stack... and will only live for the lifetime of the stack/function. Once you've created it, it is up to the presenting view controller to manage its lifetime once it goes out of scope of the function it is called in. In this case the alert needs to hold onto self, but self could care less about the alert once the function has been called, something else (the presenting view controller) is taking care of it. – Good Doug Jan 21 '16 at 22:53
  • Therefore, if the alert is a local variable it's always ok to keep a strong reference to self, otherwise if the alert is a property then it should keep a weak reference to self. Correct? – Eric Jan 21 '16 at 22:59
  • 2
    @GoodDoug but when you present it, the `presentedViewController` will be the alert, this may cause retain cycle – onmyway133 Jul 01 '16 at 12:37
  • you didn't answer the question. How would you put [weak weakSelf = self] into the handler along with the argument action in the closure. – coolcool1994 Jul 29 '16 at 19:58
  • 9
    @onmyway133 yes `presentViewController` will retain `alert`. And when `alertController` presented from `self`, why would you dealloc `self`? It is not the current view controller any more. You have to dismiss the `alertController` first. And when you do that, the retain will be release. So no retain cycle any more. – Gon Jul 30 '16 at 07:10
  • This simple rule is appropriate in this case. I've written a code and tested it in Xcode 7.3, iOS 9 – Gon Jul 30 '16 at 07:12
  • Thank you. First explanation that has made sense to me. – Iskeraet Feb 04 '21 at 14:38