When self should be weakify?

How to diagnose retain cycle and when to use [weak self] to avoid retain cycle.

Aaina jain
Swift India
4 min readJan 13, 2020

--

Credit: https://pixabay.com/

Introduction

Often we end up having a memory leak in the project. We know when our closure can increase retain count and when not. When I was executing function [Using self inside] in Notification Center Observer, I had an intuition that it won’t cause a retain cycle. As I was not calling NotificationCenter on self so I was pretty sure that it won’t cause a retain cycle but it did 😰.

How to diagnose retain cycle:

  • Debug Memory Graph: To use debug memory graph, first you need to edit scheme.
Enavle Malloc Scrabble and Malloc Stack [Live allocations only]

# Malloc scribble will show you code in Show Memory Inspector when Debug Memory Graph is launched.

# You can tap on show only leaked blocks to see only leaked classes.

  • Perform whatever app actions you want to analyse (the actions which can cause retain cycles).
Tap on Debug Memory Graph

# Open memory graph debugging mode by selecting this button.

  • The memory graph debugger pauses app execution and shows the following:
  • Or you can put breakpoint in deinit to check class has been deinitialized or not.

Let’s take some use cases which can cause retain cycle:

Use Cases:

1. Assigning closure to closure:

While using any instance property or function inside closure, compile gives us the warning to use self.

Now, while using self we need to make sure that it should be weak else it will cause a retain cycle.

Note: lazy closure can also cause a retain cycle.

Why will it cause retain cycle: Because closure captures variables which will end up in increasing retain count. If we don’t use self` inside closure then there is no retain cycle.

https://gist.github.com/aainaj/0acdb879506893991954b68478cec024
Picture from Memory graph shows retain cycle due to closure holding strong self

Link for closure without self: https://gist.github.com/aainaj/1544b9047a94b0e0015803808d58f3fc

2. Assigning function to closure:

On assigning function to closure, we assign function pointer so it actually. Function pointer is defined as: ListViewController.makeList(self). It results in a retain cycle always. So it’s good to always assign [weak self] to closure.

https://gist.github.com/aainaj/baf43fe7afb4f015561dd2bb20f0ecfd

Retain cycle can be avoided by using [weak self]

3. DispatchQueue:

Invoking closure in DispatchQueue doesn’t create a retain cycle as we are not calling this function on self`. It’s safe to use below code:

4. Async DispatchQueue:

In the previous example, We just changed the text color of a label so we may think that it won’t cause a retain cycle. But what if we are doing async API calls in dispatch queue. In this case, it doesn’t retain self but finishes all tasks submitted to the queue and then deallocate class. When a view is going off-screen, we can cancel tasks so that they don’t get executed when class is not in the hierarchy.

https://gist.github.com/aainaj/3a00c4e2959c5ce678d9582004965110

5. NotificationCenter:

As we don’t call notification center on self so it shouldn’t cause a retain cycle. But NotificationCenter.addObserver document says it copies our block so [weak self] should be used.

https://gist.github.com/aainaj/a85baa708374ae47dfbf75a0464e0de0
Retain cycle caused by notification center

6. UIView.animate:

It’s not called on self so using self inside animations block doesn’t cause a retain cycle.

Reward:

Credit: pexels.com

--

--