For an iOS developer, no one has not heard of memory leak, retain cycle, etc. are all problems related to memory loss. And it is impossible not to mention [weak self]
as a method to prevent this from happening.
1. Automatic Reference Counting.
The memory in Swift is managed by ARC mechanism, it operates by calculating the number of references to the device.
It will be freed when no object references it. So in some cases you cannot release it because there is still a strong reference to it so you should learn how to use [weak self]
Some methods to check memory leak:
- Check in deinit () function when ViewController is closed.
- Observe the increasing memory level
- Use the Allocations Instruments tool
2. Unowned, weak and Strong – weak
Closues can strongly capture any variable or constant in the context in which it is defined. For example, if you use self in closures, it will be maintained throughout the scope of that scope. And if self keeps a strong reference it will lead to a strong reference cycle.
That’s why you need to use [weak self]
or [unowned self]
to avoid that. However, be careful when using unowned, if that object nil, the app will crash.
For example:
1 2 3 4 5 | <span class="token keyword">let</span> changeColorToRed <span class="token operator">=</span> <span class="token builtin">DispatchWorkItem</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 punctuation">}</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> red <span class="token punctuation">}</span> |
3. Escaping and non-escaping closures
In Swift, there are two types of closures: escaping and non-escaping.
- Non-escaping closures execute code within its scope immediately and are not able to be stored or used later.
- Escaping closures can store it in another variable or closure and may execute it in the future.
Therefore non-escaping such as the higher order function and does not cause a reference cycle, so it will not require using weak or unowned. As with escaping, it can cause reference cyclé when not using weak or unowned. However only if it guarantees the following 2:
- Closures are stored in one variable or another closures.
- Can use self to reference in closure.
The chart below will help you see when to use [weak self]
4. Delay deallocation
In the chart above you can see the mention of delay deallocation. This is a side effect that both escaping and non-escaping can occur.
It is not a direct cause of a memory leak, but it will cause a few unwanted things. For example, when the VC is dismissed but the closure is still blocked, the VC will not be deinit at that time.
Let’s analyze with the example below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">func</span> <span class="token function">delayedAllocAsyncCall</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token function">URL</span> <span class="token punctuation">(</span> string <span class="token punctuation">:</span> <span class="token string">"https://www.google.com:81"</span> <span class="token punctuation">)</span> <span class="token operator">!</span> <span class="token keyword">let</span> sessionConfig <span class="token operator">=</span> <span class="token builtin">URLSessionConfiguration</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> sessionConfig <span class="token punctuation">.</span> timeoutIntervalForRequest <span class="token operator">=</span> <span class="token number">999.0</span> sessionConfig <span class="token punctuation">.</span> timeoutIntervalForResource <span class="token operator">=</span> <span class="token number">999.0</span> <span class="token keyword">let</span> session <span class="token operator">=</span> <span class="token function">URLSession</span> <span class="token punctuation">(</span> configuration <span class="token punctuation">:</span> sessionConfig <span class="token punctuation">)</span> <span class="token keyword">let</span> task <span class="token operator">=</span> session <span class="token punctuation">.</span> <span class="token function">downloadTask</span> <span class="token punctuation">(</span> with <span class="token punctuation">:</span> url <span class="token punctuation">)</span> <span class="token punctuation">{</span> localURL <span class="token punctuation">,</span> <span class="token number">_</span> <span class="token punctuation">,</span> error <span class="token keyword">in</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> localURL <span class="token operator">=</span> localURL <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> contents <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token keyword">try</span> <span class="token operator">?</span> <span class="token function">String</span> <span class="token punctuation">(</span> contentsOf <span class="token punctuation">:</span> localURL <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token operator">?</span> <span class="token string">"No contents"</span> <span class="token function">print</span> <span class="token punctuation">(</span> contents <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> description <span class="token punctuation">)</span> <span class="token punctuation">}</span> task <span class="token punctuation">.</span> <span class="token function">resume</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
In the above code, there is a closure and does not use weak self
or unowned self
and the closure is not stored, implemented immediately. So this case will not cause memory leak.
However, the download task will take time, so while the task is executed that ViewController is canceled, it will not be released immediately, but need to wait for the task to complete. -> Causes an unwanted delay. So you can use weak self
to prevent this from happening.
5. ‘guard let self = self’ vs Optional Chaining
When using [weak self]
we created an optional variable so when using we need to unwrap or use optional chaining. Here we will talk about how to unwrap using guard let.
If the closure has a time-consuming task and can generate a delay dealloc, the use of guard let in the first place can not avoid that. Like the following example:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">func</span> <span class="token function">process</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> <span class="token builtin">UIImage</span> <span class="token punctuation">,</span> completion <span class="token punctuation">:</span> @escaping <span class="token punctuation">(</span> <span class="token builtin">UIImage</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Void</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token builtin">DispatchQueue</span> <span class="token punctuation">.</span> <span class="token function">global</span> <span class="token punctuation">(</span> qos <span class="token punctuation">:</span> <span class="token punctuation">.</span> userInteractive <span class="token punctuation">)</span> <span class="token punctuation">.</span> async <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 punctuation">}</span> <span class="token comment">// perform expensive sequential work on the image</span> <span class="token keyword">let</span> rotated <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> <span class="token function">rotate</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> image <span class="token punctuation">)</span> <span class="token keyword">let</span> cropped <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> <span class="token function">crop</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> rotated <span class="token punctuation">)</span> <span class="token keyword">let</span> scaled <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> <span class="token function">scale</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> cropped <span class="token punctuation">)</span> <span class="token keyword">let</span> processedImage <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> scaled <span class="token punctuation">)</span> <span class="token function">completion</span> <span class="token punctuation">(</span> processedImage <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The use of guard let
is that we compare self with nil, otherwise nil we will create a strong reference to use in scope -> the cause of delay deallocation.
As for using optional chaining, it will look like:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">func</span> <span class="token function">process</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> <span class="token builtin">UIImage</span> <span class="token punctuation">,</span> completion <span class="token punctuation">:</span> @escaping <span class="token punctuation">(</span> <span class="token builtin">UIImage</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Void</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token builtin">DispatchQueue</span> <span class="token punctuation">.</span> <span class="token function">global</span> <span class="token punctuation">(</span> qos <span class="token punctuation">:</span> <span class="token punctuation">.</span> userInteractive <span class="token punctuation">)</span> <span class="token punctuation">.</span> async <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 comment">// perform expensive sequential work on the image</span> <span class="token keyword">let</span> rotated <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">rotate</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> image <span class="token punctuation">)</span> <span class="token keyword">let</span> cropped <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">crop</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> rotated <span class="token punctuation">)</span> <span class="token keyword">let</span> scaled <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">scale</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> cropped <span class="token punctuation">)</span> <span class="token keyword">let</span> processedImage <span class="token operator">=</span> <span class="token keyword">self</span> <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> image <span class="token punctuation">:</span> scaled <span class="token punctuation">)</span> <span class="token function">completion</span> <span class="token punctuation">(</span> processedImage <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Using optional chaining, we will compare self with nil at each command line, if nil will be ignored and not create a strong reference here.
6. Some examples
6.1 Grand Central Dispatch
GCD usually does not cause reference cycles unless it is stored for later use. The following example does not cause a memory leak because it executes immediately:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">func</span> <span class="token function">nonLeakyDispatchQueue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <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">1.0</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> red <span class="token punctuation">}</span> <span class="token builtin">DispatchQueue</span> <span class="token punctuation">.</span> main <span class="token punctuation">.</span> async <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> red <span class="token punctuation">}</span> <span class="token builtin">DispatchQueue</span> <span class="token punctuation">.</span> <span class="token function">global</span> <span class="token punctuation">(</span> qos <span class="token punctuation">:</span> <span class="token punctuation">.</span> background <span class="token punctuation">)</span> <span class="token punctuation">.</span> async <span class="token punctuation">{</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> navigationItem <span class="token punctuation">.</span> description <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
However, if you store GCD in a variable as follows, it will cause a leak:
1 2 3 4 5 6 | <span class="token keyword">func</span> <span class="token function">leakyDispatchQueue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> workItem <span class="token operator">=</span> <span class="token builtin">DispatchWorkItem</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> red <span class="token punctuation">}</span> <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">1.0</span> <span class="token punctuation">,</span> execute <span class="token punctuation">:</span> workItem <span class="token punctuation">)</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> workItem <span class="token operator">=</span> workItem <span class="token comment">// stored in a property</span> <span class="token punctuation">}</span> |
6.2 UIView.Animate and UIViewPropertyAnimator
Similar to GCD it also does not cause memory leak if it is not stored in the property.
Safe case:
1 2 3 4 5 6 | <span class="token keyword">func</span> <span class="token function">animteToRed</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token builtin">UIView</span> <span class="token punctuation">.</span> <span class="token function">animate</span> <span class="token punctuation">(</span> withDuration <span class="token punctuation">:</span> <span class="token number">3.0</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> red <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">func</span> <span class="token function">setupAnimation</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> anim <span class="token operator">=</span> <span class="token function">UIViewPropertyAnimator</span> <span class="token punctuation">(</span> duration <span class="token punctuation">:</span> <span class="token number">2.0</span> <span class="token punctuation">,</span> curve <span class="token punctuation">:</span> <span class="token punctuation">.</span> linear <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> red <span class="token punctuation">}</span> anim <span class="token punctuation">.</span> addCompletion <span class="token punctuation">{</span> <span class="token number">_</span> <span class="token keyword">in</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> white <span class="token punctuation">}</span> anim <span class="token punctuation">.</span> <span class="token function">startAnimation</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Case causes menory leak:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">func</span> <span class="token function">setupAnimation</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> anim <span class="token operator">=</span> <span class="token function">UIViewPropertyAnimator</span> <span class="token punctuation">(</span> duration <span class="token punctuation">:</span> <span class="token number">2.0</span> <span class="token punctuation">,</span> curve <span class="token punctuation">:</span> <span class="token punctuation">.</span> linear <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> red <span class="token punctuation">}</span> anim <span class="token punctuation">.</span> addCompletion <span class="token punctuation">{</span> <span class="token number">_</span> <span class="token keyword">in</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> backgroundColor <span class="token operator">=</span> <span class="token punctuation">.</span> white <span class="token punctuation">}</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> animationStorage <span class="token operator">=</span> anim <span class="token punctuation">}</span> |
6.3 Store closure in property
For example in ViewController1 1 closure and closure ViewController2 reference to which it should use to weak self
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">PresentedController</span> <span class="token punctuation">:</span> <span class="token builtin">UIViewController</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> closure <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">Void</span> <span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token punctuation">}</span> |
Then ViewController2 will be as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">class</span> <span class="token class-name">MainViewController</span> <span class="token punctuation">:</span> <span class="token builtin">UIViewController</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> presented <span class="token operator">=</span> <span class="token function">PresentedController</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">setupClosure</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> presented <span class="token punctuation">.</span> closure <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">self</span> <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">printer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">printer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> view <span class="token punctuation">.</span> description <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
7. Summary
Through this we can draw a few conclusions as follows:
[unowned self]
is an unsafe solution- Non-escaping does not need
[weak self]
unless it causes a dealloction delay - Escaping closure requires
[weak self]
if it is stored or passed into another closure and has a self reference in it. - GCD, aimation usually doesn’t need
[weak self]
if it’s not stored in a variable for later use. - If you are unsure then consider deinit and Instruments.
References
https://medium.com/flawless-app-stories/you-dont-always-need-weak-self-a778bec505ef