Retain cycles, weak, unowned in Swift

ref – http://www.thomashanning.com/retain-cycles-weak-unowned-swift/

  • By default, each reference, that points to an instance of a class, is a so-called strong reference.
  • As long as there is at least one strong reference pointing to an instance, this instance will not be deallocated.
  • When there’s no strong reference pointing to that instance left, the instance will be deallocated

output:
init
deinit
Program ended with exit code: 0

retain-cycle-image-1

testClass has a strong reference to an instance of TestClass. Hence TestClass instance is +1.

If we now set this reference to nil, the reference pointer will away from the TestClass object, and point to nil. TestClass instance is now 0.

strong_ref

Since there is no strong reference pointing to the object TestClass gets deallocated:

retain-cycle-image-2

By the way, if you take a look at the console, you can see that everything is working fine because the deinit method will only be called by the system when the instance gets deallocated.

If the instance of TestClass was not deallocated, there wouldn’t be the message “deinit”. As we will discuss later, placing a log message inside of deinit is a very good way to observe the deallocation of an object.

Retain Cycle Example

Now, we have two reference variables called testClass1, and testClass2.
testClass1 reference variable point to a newly allocated object TestClass.
testClass2 reference variable point to a newly allocated object TestClass.

the object pointed to by testClass1, has a property called next, which points a TestClass object. It then points to the object referenced by testClass2.

same applies to the object pointed to by testClass2.

Hence, so far each object has 2 strong references pointing to it.

The situation is visualized in the following picture:

retain-cycle-image-3

TestClass instance pointed to by testClass1, has ref count 2.
TestClass instance pointed to by testClass2, also has ref count 2.

ref_cycle

Now say we are done using these objects and we set our reference variables testClass1 and testClass2 to nil:

But the two instances won’t get deallocated! You can see this because there are not “deinit” messages in the console. Why is this happening? Let’s take a look at the situation:

retain-cycle-image-4

Each object has lost one strong reference, but because it is pointed to by the next property of each other, each object still has 1 strong reference pointing to it. Thus, both object still has +1.

This means these two objects won’t be deallocated. This is called a memory leak. If you have several leaks in your app, the memory usage of the app will increase every time you use the app. When the memory usage is to high, iOS will kill the app. That’s the reason why it’s so important to take care of retain cycles. So how can we prevent them?

Solution – using weak

Using so-called weak references is a way to avoid retain cycles. If you declare a reference as weak, it’s not a strong reference. Thus, by definition, a weak reference does not increment the reference count of an object. Only strong reference increment the count.

Let’s change our next property to weak, and see what happens:

From a reference count point of view:

weak_ref_cycle

Now, let’s set it to nil and see what happens.

Thus, now we set the reference to the objects to nil. This is -1 for the count. Due to weak having 0 effect on the reference count, the objects becomes 0 in reference count. Since the next property is weak, and we already niled the strong references testClass1 and testClass2, the two objects then becomes 0 reference count, and thus gets deallocated.

retain-cycle-image-5

Only weak references are left and the instances will be deallocated.

Because only optionals can become nil, every weak variable has to be an optional.

unowned

Besides weak, there is a second modifier that can be applied to a variable: unowned. It does the same as weak with one exception: The variable will not become nil and therefore the variable must not be an optional. It must be a non-optional variable.

But as I explained in the previous paragraph, the app will crash at runtime when you try to access the variable after its instance has been deallocated. That means, you should only use unowned when you are sure, that this variable will never be accessed after the corresponding instance has been deallocated.

Generally speaking it’s always safer to use weak. However, if you don’t want the variable to be weak AND you are sure that it can’t be accessed after the corresponding instance has been deallocated, you can use unowned.

It’s a little bit like using implicitly unwrapped optionals and try!: You can use them, but in almost all cases it’s not a good idea.

Common scenarios for retain cycles: delegates

retain-cycle-delegate

Essentially,

retain-cycle-image-6

retain-cycle-image-6b

So remember that you should in almost all cases declare delegates as weak to prevent retain cycles.

Here’s a look at the reference count perspective:

delegate_ref_cycle1

delegate_ref_cycle2

Retain cycles in closures

First, some semantics…

As a function parameter with explicit capture semantics:

As a function parameter with explicit capture semantics and inferred parameters / return type:

We can see in the logs that the instance of TestClass will not be deallocated. The problem is, that TestClass has a strong reference to the closure and the closure has a strong reference to TestClass:

You can solve this by capturing the self reference as weak:

In reference count perspective:

If we were to strong references, the object strongs the closure object, and the closure object strongs the TestClass object. Thus, when the object reference to TestClass gets pointed away, our TestClass still has +1 reference pointed to it by the closure.

block_cycle_1

If we are to use weak reference in the closure, TestClass object only has +1 reference count due to its instance reference. When we point away the instance reference, then it becomes 0, and thus everything gets deallocated, including its reference to the closure.

block_cycle_2

Local reference to closures

However, there won’t always be a retain cycle when using closures! For example, if you are just locally using the block, there is no need to capture self a weak:

The reason is that variable aBlock is created and pushed onto the local stack. Yes, it strongs the closure object. But when the local stack goes out of scope every local variable gets popped (including the strong reference to the closure). Thus, when it gets popped, all you’re left with is the closure object in the heap, with its strong reference back to TestClass so that it can use it.

Then once the closure object finishes executing, it sees that nothing is referencing it. Thus it gets deallocated, including its strong reference to TestClass.

local_stack_ref_closure

The same holds true when use for example UIView.animateWithDuration:

The idea here is the same. Locally, we’re simply referencing UIView’s class function. And that class function references the closure object. Once our local stack gets popped, the strong reference to UIView’s class function gets niled.

a child class should not have a strong reference to the parent class