Delayed Deallocation

You may have noticed the box at the left of the flowchart that mentions delayed deallocation. This is a side effect that comes with both escaping and non-escaping closures. It is not exactly a memory leak, but it might lead to undesired behavior (e.g. you dismiss a controller, but its memory doesn’t get freed up until all pending closures/operations are completed.)

Since closures, by default, will strongly capture any objects referenced in their body, this means they will impede these objects from getting deallocated from memory for as long as the closure body or scope is alive.

The lifetime of the closure scope can range from under a millisecond up to several minutes or more.

Here are some scenarios that can keep the scope alive:

A closure (escaping or non-escaping) might be doing some expensive serial work, thereby delaying its scope from returning until all the work is completed A closure (escaping or non-escaping) might employ some thread blocking mechanism (such as DispatchSemaphore) that can delay or prevent its scope from returning An escaping closure might be scheduled to execute after a delay (e.g. DispatchQueue.asyncAfter or UIViewPropertyAnimator.startAnimation(afterDelay:) ) An escaping closure might expect a callback with a long timeout (e.g. URLSession timeoutIntervalForResource )

There are probably other cases I have missed, but this should at least give you an idea of what might happen. Here’s an example from my demo app showing URLSession delaying deallocation:

Let’s break down the example above:

I am purposely calling port 81 (a blocked port) to simulate a request timeout

The request has a 999 seconds timeout interval

No weak or unowned keyword is used

or keyword is used self is referenced inside the task closure

is referenced inside the task closure The task is not stored anywhere; it’s executed immediately

Based on the last point above, this task should not cause a strong reference cycle. However, if you run the demo app with the above scenario, and then dismiss the controller without canceling that download task, you will receive an alert saying the controller memory was not freed.

So what exactly happened here?

We are running into Scenario #4 from the list mentioned earlier. That is, we have an escaping closure that is expecting to be called back, and we gave it a long timeout interval. This closure keeps a strong reference to any objects referenced inside it ( self in this case), until it gets called or reaches the timeout deadline, or if the task gets canceled.

(I’m not sure how URLSession works behind the scenes, but I’m guessing it keeps a strong reference to the task until it gets executed, canceled or reaches the deadline.)

There is no strong reference cycle here, but this closure will keep self alive for as long as it needs it, thereby potentially delaying self ’s deallocation if the controller gets dismissed while the download task is still pending.

Using [weak self] (along with optional chaining or guard let syntax) would prevent the delay, allowing self to get deallocated immediately. [unowned self] on the other hand, would cause a crash here.

‘guard let self = self’ vs Optional Chaining

When using [weak self] , there is a potential side effect to using guard let self = self instead of accessing self using optional chaining self?. syntax.

In closures that can delay deallocation due to expensive serial work, or due to a thread blocking mechanism like a semaphore (scenarios #1 and #2 in the list mentioned earlier), using guard let self = self else { return } at the start of the closure would not prevent this deallocation delay.

To illustrate why let’s say we have a closure that performs several expensive operations in succession on a UIImage:

guard let syntax can lead to delayed deallocation

We are using [weak self] along with guard let syntax at the start of the closure. What guard let actually does here is it checks whether self is equal to nil , and if it isn’t, it creates a temporary strong reference to self for the duration of the scope.

By the time we reach the expensive work (lines 5 and below), we have already created a strong reference to self , which will prevent self from deallocating until we reach the end of the closure scope. Said differently, guard let will guarantee that self stays around for the life of the closure.

If we do not use guard let syntax, and instead use optional chaining with self?. notation to access the methods on self, the nil check for self would happen at every method call instead of creating a strong reference at the start. This means that if self happens to be nil at any point during the execution of the closure, it will silently skip that method call and go to the next line.

Optional chaining leads to no delayed deallocation

It’s a rather subtle difference, but I think it’s worth pointing out for cases where you may want to avoid unnecessary work after a view controller gets dismissed, and on the flip side for cases where you want to ensure that all the work gets completed before an object gets deallocated (e.g. to prevent data corruption.)

Examples

I will go through some examples from the demo app showing common situations where [weak self] may or may not be needed.

Grand Central Dispatch

GCD calls generally do not pose a risk of reference cycles, unless they are stored to be run later.

For example, none of these calls will cause a memory leak, even without [weak self] , because they are executed immediately:

However, the following DispatchWorkItem will cause a leak, because we are storing it in local property, and referencing self inside the closure without the [weak self] keyword:

UIView.Animate and UIViewPropertyAnimator

Similar to GCD, animation calls generally do not pose a risk of reference cycles, unless you store a UIViewPropertyAnimator in a property.

For example, these calls are safe:

The following method, on the other hand, will cause a strong reference cycle, because we are storing the animation for later use without using [weak self] :

Storing a function in a property

The following example demonstrates a cunning memory leak that can hide in plain sight.

It can be useful to pass closures or functions of one object to a different object, to be stored in a property. Let’s say you want object A to call some method from object B anonymously, without exposing object B to A. Think of it like a lightweight alternative to delegation.

As an example, here we have a presented controller that stores a closure in a property:

We also have a main controller (which owns the above controller), and we want to pass one of the main controller’s methods to be stored in the presented controller’s closure:

printer() is a function on the main controller, and we assigned this function to the closure property. Notice how we didn’t include the () parentheses in line 6 because we are assigning the function itself, not the return value of the function. Calling the closure from inside the presented controller will now print the main’s description.

Cunningly, this code introduces a strong reference cycle even though we didn’t explicitly use self . The self is implied here (think of it like self.printer ), therefore the closure will maintain a strong reference to self.printer , while self also owns the presented controller which in turn owns the closure.

To break the cycle, we need to modify setupClosure to include [weak self]

Note that we are including the parentheses after printer this time, because we want to call that function inside the scope.

Timers

Timers are interesting, as they can cause issues even if you don’t store them in a property. Let’s take this timer for example:

This timer repeats self is referenced in the closure without using [weak self]

As long as these two conditions are met, the timer will prevent the referenced controller/objects from deallocating. So technically, this is more of a delayed allocation rather than a memory leak; the delay just happens to last indefinitely.

Be sure to invalidate your timers when they’re no longer needed to avoid keeping their referenced objects alive indefinitely and don’t forget to use [weak self] if any of the referenced objects keep a strong reference to the timer.

Demo App

There are other examples in the demo app, but I think this article is already long enough as it is, so I won’t cover all of them. I encourage you to clone the app and open it in Xcode, then check out the different leak scenarios in PresentedController.swift (I added comments explaining each scenario.)

Notice how when you run the app with a leaky scenario, your app memory usage will steadily increase as you present and dismiss controllers

The staircase is not a good sign!

Alternatives to [weak self]

Before I conclude, I just want to mention a couple of tricks that you can use if you don’t want to bother dealing with [weak self] (I learned about them from these excellent articles by objc.io and swiftbysundell)

First, instead of passing self directly to closure and having to deal with [weak self] , you can create a reference to the property that you want to access in self , and then pass that reference to the closure.

Let’s say we wanted to access the view property on self inside an animation closure. Here’s what that could look like:

We create a reference to the view property on line 2 and use it inside the closures in lines 4 and 7 instead of using self . The animation then gets stored as a property on self in line 9, but the view object doesn’t have a strong reference to the animation, so no circular reference takes place.

If you want to reference multiple properties on self in the closure, you can group them all together in a tuple (let’s call it context), then pass that context to the closure:

Conclusion

Congratulations on making it this far! The article ended up being far longer than I originally planned for 😅

Here are some key takeaways:

[unowned self] is almost always a bad idea

is almost always a bad idea Non-escaping closures do not require [weak self] unless you care about delayed deallocation

you care about delayed deallocation Escaping closures require [weak self] if they get stored somewhere or get passed to another closure and an object inside them keeps a reference to the closure

if they get stored somewhere or get passed to another closure an object inside them keeps a reference to the closure guard let self = self can lead to delayed deallocation in some cases, which can be good or bad depending on your intentions

can lead to delayed deallocation in some cases, which can be good or bad depending on your intentions GCD and animation calls generally do not require [weak self] unless you store them in a property for later use

you store them in a property for later use Be careful with timers!

If you aren’t sure, deinit and Instruments are your friends

I think the flowchart I posted earlier also provides a helpful recap of when to use [weak self]

Update: I revisted the topic of [weak self] in my new post here, which covers nested closures in Swift.