⬆️ ⬇️

Do I need to write weak self in Grand Central Dispatch?

Here we have a dispute: do I need to write [weak self] in GCD?



One says:

- [weak self] need to write everywhere!

The second says:

- No, even if you do not write [weak self] inside the DispatchQueue, there will be no memory leak.



Instead of understanding, it is easier to write a couple of lines. It’s harder to write a post.

So, we will create a UIViewController in which the method in the DispatchQueue will be called five seconds after viewDidLoad.

')

class SecondViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() print("viewDidLoad") DispatchQueue.main.asyncAfter(deadline: .now()+5, execute: { [weak self] in self?.method() }) } func method() { print("method") } deinit { print("deinit") } } 


This ViewController will be fired from another ViewController. And the point is that in these five seconds, before calling our method, we have to remove this ViewController from the UINavigationController stack. That is, just push back.



After starting in the console we see:

viewDidLoad

deinit


That is, after creating our ViewController, viewDidLoad was called . Then, after clicking back, our ViewController retired from memory and volunteered to deinit . And our method in DispatchQueue was not called from the 19th line, because at this moment our ViewController-a no longer exists, self is equal to nil.



Now let's see what happens if we remove [weak self] from the DispatchQueue and leave it that way.



 class SecondViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() print("viewDidLoad") DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: { self.method() }) } func method() { print("method") } deinit { print("deinit") } } 


Console:

viewDidLoad

method

deinit


Called by viewDidLoad. After five seconds, our method is executed and only then the ViewController is de-injected. That is, after clicking back, our ViewController lives until the method is executed and only then it is released. But memory leaks do not occur! Because in the end he retired.



And what will happen if in the DispatchQueue to pass some kind of closure. Like this:



 class SecondViewController: UIViewController { var closure: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() print("viewDidLoad") closure = { print("closure") } DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: closure!) } func method() { print("method") } deinit { print("deinit") } } 


Output:

viewDidLoad

deinit

closure


Called by viewDidLoad. Then the ViewController is removed. And after five seconds our closure is executed. That is, it does not matter whether ViewController is alive or not. It does not have a link to our ViewController. He will call anyway.



And how should there be a leak? It is necessary that our closure call the ViewController method, that is, have a link to it.



 class SecondViewController: UIViewController { var closure: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() print("viewDidLoad") closure = { self.method() } DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: closure!) } func method() { print("method") } deinit { print("deinit") } } 


In the console:

viewDidLoad

method


So, in the end, deinit did not volunteer and we received a memory leak. And to get rid of it, you only need to write [weak self] in closure.



 class SecondViewController: UIViewController { var closure: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() print("viewDidLoad") closure = { [weak self] in self?.method() } DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: closure!) } func method() { print("method") } deinit { print("deinit") } } 


Console:

viewDidLoad

deinit


Total



It doesn't matter if you write [weak self] in GCD or not, there will be no memory leak. But you need to know that their behavior is different. In the first case, what's inside Dispatch-a will not be fulfilled. And in the second - it will be fulfilled, but before its execution the ViewController will live.

Source: https://habr.com/ru/post/452512/



All Articles