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
1 2 | <span class="token keyword">var</span> someClosure <span class="token punctuation">:</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Int</span> <span class="token punctuation">)</span> <span class="token operator">?</span> |
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.
1 2 3 4 | <span class="token keyword">var</span> someClosure <span class="token punctuation">:</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Int</span> <span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> someClosure <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span> <span class="token keyword">weak</span> <span class="token keyword">self</span> <span class="token punctuation">]</span> <span class="token keyword">in</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> ` <span class="token keyword">self</span> ` <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">0</span> <span class="token keyword">return</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> a <span class="token operator">+</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> b <span class="token punctuation">}</span> |
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:
- The Navigation controller removes ViewController from the stack and reduces the reference count to 0.
- ARC finds the object without reference and sees ViewController then removes it from memory and also removes the links that it owns.
- 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
- The Navigation Controller holds a strong reference to the SecondViewController making the reference number 1.
- A strong self closing closure causes the SecondViewController object reference number to increase to 0
- The newSomeClosure variable holds a strong reference to the closure making RF = 1.
Now see what happens when we popViewController:
- SecondViewController RF = 1
- When the closure executes and is internally kept, it is removed from the stack. Closure RF will reduce by 1 and return to 0.
- 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
.
1 2 3 4 5 6 | <span class="token builtin">DispatchQueue</span> <span class="token punctuation">.</span> main <span class="token punctuation">.</span> <span class="token function">asyncAfter</span> <span class="token punctuation">(</span> deadline <span class="token punctuation">:</span> <span class="token punctuation">.</span> <span class="token function">now</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> <span class="token function">execute</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
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:
1 2 3 4 5 | <span class="token builtin">SingletonClass</span> <span class="token punctuation">.</span> shared <span class="token punctuation">.</span> <span class="token function">someSingletonMethod</span> <span class="token punctuation">(</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> a <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span> value <span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> <span class="token function">execute</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
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:
1 2 3 4 5 6 | SingletonClass.shared.someSingletonMemoryLeakMethod(self.a) { (value) in self.execute() } |
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.