Retain Cycle in Swift

Mehmet Ateş
IBTech
Published in
6 min readOct 31, 2022

Memories. Indispensable parts of our technological devices. How well do we manage them as developers? Let’s learn what Retain Cycle is, which is a part of this topic today, and how we can manage it.

Our first topic is ARC

We just said retain cycle, what’s the point of ARC? A.R.C, namely Automatic Reference Counting, is a kind of memory cleaning tool that Apple has presented to us. It cleans up our redundant references and frees us up a good memory. If we write the kind of code that can use it, of course.

If we write the appropriate code, there is no problem anyway. We couldn’t write, or we didn’t know. So what will we do 😱? Let’s see.

I have written a small project for you so that you can experience this beautifully. Please feel free to fork the project and experience it with me.

All we have to do is run the project first and go through the pages a few times. You won’t be able to do anything else anyway ☺️.

  • For example, go to the last page and then back to the first page. Then click on the button(Debug Memory Graph) shown below.
  • Your application will stop. And your Xcode will look like this. See where I’ve yellow-marked below. We are now on the first page, but there are precisely 4 view models in memory.
  • For a better understanding, press the button(Continue) I marked in red and let the application continue. Then repeat the first navigate operation 2 more times. And click the Debug Memory Graph button again.
  • Our number has reached 10! So why 10 do you think? Our first page in existence has never died. But the 3 pages we created by going forward died every time we came back. We repeated the process 3 times in total and since we have 3 pages, we have created 9 view models. Of course, the view model of our first page that never died. Thus, a total of 10 view models are currently kept in memory. Nightmare.

So far, I hope you understand everything. If you don’t understand, repeat the steps and continue until you do. So let’s continue. What caused this?

The above code is common to all view controllers. They all init a view model.

All view models derive from a single class and require a view reference to itself in the common view model. This means that for us.

View controller has view model and view model has view Controller 🤯. And yes! Now you know what retain cycle.

Okay, but there is a way out of this, right? Because this is really scary. Of course there is. With only a one-word type designation. Let’s learn the bride types first and then get rid of this scourge.

Reference types are divided into 3. Shall we meet them?

  • Strong: I am a strong reference 💪🏼. Whether a class holds me or not, I keep that class. And I won’t be lost until you destroy me. (Currently, we have two classes of this type.)
  • Weak: Hi I’m weak reference 🥲. If a class doesn’t hold me, I don’t exist. I live with it, I die with it.
  • Unowned: Hi, I’m unowned 🤐. I expect a class to hold me. But if you want to use me, make sure I go before the class holding me. If you don’t I will crash your app 😵.

Which one do you think we should use? Our type is strong anyway so we need to use it. Weak or Unowned? Let’s try both in our case.

Let’s start with the Weak. Open the CommonViewModel as below and set the view to weak.

Run the application again and go to the last page 3 times in the same way and come back. Then open the Debug Memory Graph.

Fantastic. We are currently on the first page and only one view model is in memory, just as we wanted. Also there are 9 outputs in the debug logs.

Hi, Am i dead? Yes :( //Page 3
Hi, Am i dead? Yes :( //Page 2
Hi, Am i dead? Yes :( //Page 1
Hi, Am i dead? Yes :( //Page 3
Hi, Am i dead? Yes :( //Page 2
Hi, Am i dead? Yes :( //Page 1
Hi, Am i dead? Yes :( //Page 3
Hi, Am i dead? Yes :( //Page 2
Hi, Am i dead? Yes :( //Page 1

So how did this happen? Our View models have a deinit function that will do this print every time it disappears. So it only works when they die. Since we defined it as weak, view now holds the view model. And so when the view dies, the view model also dies. Well, it’s great.

Now let’s try the same for unowned. The result will be the same. But let’s change one more thing and understand the situation better. Let’s write a deinit function inside the whole view controller.

Like this. And then let’s go to the last page and back to the first page. Repeat this time only once.

This is how we will see the Debug console. As you can see, the view I gave as unowned dies before the viewmodel, so the application runs smoothly without any problems.

How about a little reinforcement? 🧐

We have 2 more branches to understand the subject better. You can switch to them as follows. (Or you can make changes from a git-client of your choice and examine the code.)

First, let’s move on to our bonus branch. What did we change? You can view them all here, but I’ll summarize.

What I did was create a view model on root and distribute it to other views. And the view model could never be created again because the root page never died. In this way, even though I did not specify weak or unowned on the view model, the retain cycle did not occur.

Let’s take a look at bonus2, too. The only thing I did here was to create the view model I shared in page1, not root. Again all the changes are here.

So what did that change? Creating and sharing a view model in Page1 has reduced the number of retain cycles that occur, but it will continue to occur as long as I don’t use weak. Because page1 can die and therefore the view model is unfortunately freed in memory.

In summary, weak or unowned are very important types that we should use in such cases. But it is also very important that we use it in the right place and in the right way. This is something we can get by experiencing a lot of things 🦸🏼.

Closing

We discussed the subject of retain cycle, which is a very complex subject to explain as well as to understand. I hope everything is clear in your mind. See you on future issues. Happy coding, with Swift 🧡.

Resource(s)

--

--