Things you need to know about Memory Leaks in iOS (Part 2)

Tram Ho

Part 2

Memory leak in Closure

When we create a closure with the value it needs to use it requires keeping a strong reference to that value if the value needed for closure and closure is the component of the controller.

As shown below, we create an optional closure as an attribute of the controller with type () -> Int and register that value in viewDidLoad. Closure needs properties a and b to execute. Also a and b are properties of a class that will capture class references via the self keyword. These are the values ​​it needs externally, and keep those values ​​usable

First, we have a self.someClosure that stores a strong reference to closure:

someClosure = { return self.a + self.b }

Since then, ViewController and Closure both refer to ARC reference counting system type, counting on both.

To execute the statement, we define the closure and assign it to the properties of the class that holds a strong reference to the closure and increase the closure reference number to 1 in the image below. Since the Controller is pushed from the navigation controller. Navigation Controller creates a strong reference to the ViewController where increasing the reference number by 1 prevents the ViewController from being freed.

From the moment the closure needs the a and b properties it holds / takes a reference to the class which makes the class reference number increase by 2.

When you pop the ViewController, it is removed from the navigation stack, which reduces the reference number by 1.

In fact, ViewController deinit should be called but we have created retain cycle. It happens when we create a reference loop between two or more objects to each other as shown.

ARC finds and doesn’t find the reference number 0 so it won’t free both from memory:

Solution

Causes one of the references to become weak / unowned and the other to be a strong reference, and thus, the reference circle will be broken.

See the image below, closure now captures weak reference to self. From there we use weak with making self an optional type which is the reason to use guard to unwarp safely the value of self.

Navigation coordinates 1 strong reference to SecondViewController causes reference number to increase by 1 and SecondViewController has the closure since the closure is the class’s attribute, the reference number to the closure is 1. But now the clossure captures weak self as we intended. means in closure so the reference number won’t increase as shown:

Now, after popViewController will take place the following operations:

  1. The Navigation controller removes ViewController from the stack and reduces the reference count to 0.
  2. ARC finds the object without reference and sees ViewController then removes it from memory and also removes the links that it owns.
  3. Because the strong and weak references of the ViewController are also removed, the closure of the closure reference counter is reduced to 0 and finally removed from the memory.

The difference between weak and unowned

Unowned

Like the weak reference, the unowned reference doesn’t hold a strong reference to the object it points to (meaning it can be used to break a reference loop).

Unlike the weak reference, however, an unowned reference is used when the other object has a similar or longer life cycle. Put in the following case if you are sure that when the closure executes self successfully otherwise the application will crash. OR self has a longer life cycle than closure

Download the sample application

Run the application >> it will display the screen and have a button >> touch the button it will push SecondViewController >> In viewDidLoad it will execute someMethodThatTakeClosure it takes 4 seconds. After 4 seconds it will execute the closure and require self so after 4 seconds if we pop the secondViewController the application will crash as shown below since the closure has a longer life time and the SecondViewController is also canceled:

Repair

Using weak self will use self to be optional and can be guarded to open it. If self is actually canceled we can handle it

Rules of memory

Use weak if you are not sure whether the self you captured in the closure may or may not be available in some cases

Use unowned if you are sure that 100% self will always be available when you execute closure

Rule 2:

When you have closures inside the class and you want to access anything that needs self use the self via weak / unowned .

Do not leak memory in case 1

Closure function captures self but does not cause leakage As shown below we create closure within someMethod internally when closure is no longer a property of controller, controller does not hold strong reference to closure but closure captures self strongly which means When executing the controller, it will deallocate itself. In this case, closures have a longer life cycle

  1. The Navigation Controller holds a strong reference to the SecondViewController making the reference number 1.
  2. A strong self closing closure causes the SecondViewController object reference number to increase to 0
  3. The newSomeClosure variable holds a strong reference to the closure making RF = 1.

Now see what happens when we popViewController:

  1. SecondViewController RF = 1
  2. When the closure executes and is internally kept, it is removed from the stack. Closure RF will reduce by 1 and return to 0.
  3. Closure RF = 0 will delete everything it is holding from it and cause SecondViewController RF = 0 and cause ViewController to be released.

No memory leak case 2

The static method will capture self without causing any memory leak. self does not own the static class but the static closure class takes strong self

Static Class -> Closure -> Self (does not create memory leak)

The code below will not leak any memory since the Class doesn’t own DispatchQueue .

Memory leak

Rule of Thumb:

If you use Singleton / Static class to keep closure in its class. The time it keeps closing memory will be lost during this time. As illustrated below, we have a singleton class that will keep the closure in someSingletonMethod and after executing the closure, the method scope will end and the closure will be freed memory after the local variable holding the closure is released:

We have a singleton class that will keep the closure in someSingletonMemoryLeakMethod and when executed, it holds a strong reference to the closure as the class attribute and after executing the closure, it will not be released by the Singleton class which always exists in memory and it will keep the closure and create a memory leak. In this scenario, a memory leak error occurs.

Memory Leak = SingletonClass -> Closure -> Self

Until SingletonClass exists in Memory, self (ViewController) will exist in memory.

When we pop the controller will not be deadllocate since the closure will keep it strong it will be stored and not released by the Singleton Class:

Solution

Use weak self to avoid memory leak:

No Memory Leak = SingletonClass → Closure

From now on SingletonClass only keeps reference to closure since closure weak self when we pop controller it will deallocate because it only owns navigation controller.

This example will go to the static class:

Tracing Memory Leak using Memory Graph Debugger

We will find ways to fix errors we will find errors and use them using technology first. The main task is to use the Memory Graph Debugger to find errors in the project. Download the example project.

Memory Graph Debugger

To find a way, let us answer the question: Why does the object exist in memory?

Memory graph debugger helps to find and fix reference loops and memory leaks. When activated it will stop the application, and display the objects that exist in the heap, and the reference will keep them existing.

Open the downloaded project and run the application, tap the button will redirect to the new screen. Tap from the time you touch the screen and have the word “Third View Controller Title”, now touching the Third View Controller button will show “Second View Controller” .Then tap back you need to see the memory leak in Third View Controller Controller or not.

As shown below, now in SecondViewController but ThirdViewController is still in memory which means it has not been freed. Touching it, we’ll define several closures that keep it strong:

We will find it in ViewDidLoad:

Now run the application again and follow the steps, there will be no memory leak. As the picture no longer has the ThirdViewController in the heap, it means it has been freed:

We have addressed memory leaks, and there are still a few places causing them in controllers. Run the application again and go to ThirdViewController and tap Button 1. This will create a memory leak inside it as shown below.

Summary:

To find memory leaks you need to make every flow your controller will make and check the memory graph debugger heap or print something indefinite.

So the second part of the translation of the article by Ali Akhtar, thank you for taking the time to read.

Share the news now

Source : Viblo