9
votes

I have a question regarding memory deallocation and blocks/closures.

Following is the Swift method

    self!.dismissViewControllerAnimated(false, completion: {
        println(self);
     })

Or the objective C method

   [self dismissViewControllerAnimated:NO completion:^{
       NSLog("%@",self);
   }];

I would really appreciate if anyone could explain when in the above method self would be deallocated . Is it after the completion block is run or before that? I understand its taken care by ARC but I would like to know if self gets release message in the completion block or after that. Hence, if I do some minor clean up in the completion block (accessing self), is that safe/acceptable or not?

2
It would definitely be after the dismissing has completed. Though not necessarily immediately after. - CrimsonChris
If you reference self inside that closure, the closure will retain a reference to the dismissed view controller. The act of putting the clean up there should guarantee that your view controller isn't deallocated until after the closure has been executed. - Nate Cook
See this link it's a GREAT read on closure capturing. - Honey

2 Answers

7
votes

There are really two separate questions to understand the answer completely:

1. When Are Variables Referenced Inside of Closures Released?

You can think of a closures as just another type (in Swift they really are). Every closure creates a strong ownership over the variables referenced inside of it, including self. This ensures that you never reference a variable that has been deallocated. Just like in any other object, when the closure gets deallocated, it releases its ownership over all of its variables.

If the closure is the only thing with a strong reference to that a variable, the variable will be dealloced when the closure is dealloced.

So in short, variables stay in memory as long as the closure is still in memory.

2. When Are Closures Deallocated?

Now the second part of this that is important to understand, is when does a closure get dealloced. You could store a closure in a variable:

func myMethod() {
    var myClosure = {
        println(self)
    }
    myClosure()
}

In this case, the closure has a strong reference from its variable myClosure and the closure has a strong reference to self. As soon as myClosure goes out of scope, i.e. when myMethod exits, it will be dealloced. When that happens, self will be released.

You may also have a situation, like in your question, where you are passing a closure into another method. In this case, the method you are passing the closure into is capturing a strong reference to your closure. When that method exits, it will release your closure, the closure will be deallocated, and it will release all variables captured inside it.

The Exception

Sometimes it is necessary to define a closure that does not take ownership over a variable. You would do this to avoid circular references. You can read more in Apple's documentation on what a circular reference is and how to prevent them here. However, it is important to realize that unless you put in explicit effort (and code), closures will always capture strong references to variables referenced inside of it.

2
votes

In Swift, while developing/debugging and trying to understand the timing of operations, you can add the following to your View Controllers (or any class), to track if or when the instance is a about to deallocated.

Although that won't help you detect 'strong reference cycles' described here:

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

deinit {
    println(__FUNCTION__, "\(self)")
}