Memory Graph Debugger

If you know ARC and what is memory leaks in iOS you can start from section 2. This is a very long article in this article we will look:

  1. What is memory Leaks in iOS
  2. Why Memory Leaks
  3. How ARC unable to free memory
  4. Memory Leaks in Closure
  5. Possible Solution
  6. Some Special Scenarios (Singleton and Static Classes Memory Leaks)
  7. Difference Between weak and unowned
  8. Identify Leaks Using Memory Graph Debugger
  9. Some Rule of Thumbs
Section 1
Memory Leaks in iOS
A memory leak occurs when a given memory space cannot be recovered by the ARC (Automatic Reference Count)because it is unable to tell if this memory space is actually in use or not. One of the most common problems that generate memory leaks in iOS is retained cycles we will see it later.
How ARC is unable to Free memory?
Every time you create a new instance of a class, ARC allocates a chunk of memory to store information about that instance. When an instance is no longer needed it frees up the memory so the question is how ARC identifies the instance is no longer needed. To understand it we first need to look at how ARC internally works.

Every variable is declared as strong by default. When you create an instance of a class with strong it increments the reference count of that instance. The reference count is the number of strong references to that instance. When ARC sees any variable that has Reference count to 0 it frees up the memory of that instance memory:
var referenceOne: Person? = Person( )
As shown above we create an instance of Person object. ARC will allocate memory and store reference to referenceOne variable since that variable holds a strong reference to Person object instance ARC will increment the reference count to 1 as shown in Figure 1:

Figure 1

var reference2 = referenceOne
After executing the above statement we create another strong reference to Person object instance by assigning its address to reference2 variable. As shown in Figure 2 now reference count = 2 after assigning another strong reference to the Person object:

Figure 2

referenceOne = nil
After executing the above statement we removed a strong reference to Person object by assigning nil value to referenceOne variable to break the strong reference. As shown in Figure 3 now reference count = 1 after removing strong reference to the Person object:

Figure 3

reference2 = nil
After executing the above statement we removed a strong reference to Person object by assigning nil value to reference2 variable to break the strong reference. As shown in Figure 4 now reference count = 0 after removing strong reference to the Person object:

Figure 4

When Reference Count = 0 ARC will remove the Person object from memory. This is how ARC works.
Still question How memory will leak
As we understood how ARC works if it sees any variable with Reference Count = 0 it free the space what if Reference Count never goes to 0 it’s possible to write code in which an instance of a class never gets to a point where it has zero strong references. So let's take an example and reproduce this case.
Question: When ARC Try To See Variable RC (Reference Count)
When you are working with cocoa or cocoa touch application is simply to assume that your run loop is gonna run any time you give up control of the thread of execution in your application, so any time you exit a method or there are certain methods you can call on NSObject that will run the current run loop (for loop) , anytime you do anything like that you have to assume that autorelease pool is going to be drained. At each drained they check if variable has RC = 0 it dealloc it. In Short At the end of every run loop it check this
Rule of thumb : If no one is owning an object ARC will free up the memory OR
If no one has strong reference to an instance of an object ARC will free up the memory OR
If an object Reference Count = 0 ARC will free up the memory OR
If an object has zero strong reference, the object will free from the memory
Here’s an example as of how a strong reference cycle (means Reference count never reaches zero)can be created by accident. As shown in Figure 5 we define two classes called User and Todo. User has todo property which stores the strong reference to todo object as we said earlier by default property created with a strong pointer to an object:

Figure 5

var user: User? = User() //reference count 1 User object
var todo: Todo? = Todo() //Reference count 1 Todo Object
By executing the above statements we created an instance of User and Todo object. ARC will allocate memory and store strong reference of the User object to user variable since that variable holds a strong reference to User object instance ARC will increment the reference count to 1 same goes to todo object. As shown in Figure 1 user variable holds a strong reference of User object whereas todo object is holding Todo object instance:

Figure 6

user?.todo = todo //reference count 2 Todo Object
todo?.associatedUser = user //reference count 2 User Object
By executing the above statements we performed the following actions as shown in Figure 7:

  1. First, we increment the Todo object Reference count which now has two strong references pointing to it which makes it reference count to 2 which also means Todo objects have two owners.
  2. Second, we increment the User object Reference count which now has two strong references pointing to it which makes it reference count to 2.

Figure 7

user = nil //reference count 1 User object 
todo = nil //reference count 1 Todo object
Ideally, when you set Object to nil it’s deinitializer should be called. In these scenarios, neither deinitializer was called when you set these two variables to nil The strong references between the User instance and the Todo instance remain and cannot be broken and there is no way to free these objects' memory. This is called Strong Reference cycle:

Figure 8

Solution
There are two solutions to this problem make one of the property to weak or unowned. Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a stronghold on it. The instances can then refer to each other without creating a strong reference cycle. As compared to strong weak does not increment reference count.

As shown in Figure 9 we created weak property name associatedUser in Todo class which means assigning User object to this property will not increment it’s reference count / will not hold a strong reference to User object:

Figure 9

var user: User? = User() //reference count 1 User object
var todo: Todo? = Todo() //reference count 1 Todo Object
By executing above statements we created User and Todo objects in user and todo variable holding a strong reference to these objects respectively which makes both objects reference count to 1:

Figure 10

user?.todo = todo //reference count 2 Todo Object
todo?.associatedUser = user //reference count 1 User object
Here’s how the references look now that you’ve linked the two instances together as shown in Figure 11 User has strong reference to TodoObject which make it Reference Count to 2 whereas Todo object has weak reference to User object which doesn’t change its reference count:

Figure 11

user = nil
Setting user to nil will decreases the reference count of User object and make it to 0:

Figure 12

Now ARC while checking all objects Reference count found User object reference count 0, it frees up the memory and associated reference ownerships/strong reference as shown in Figure 13. Todo object with associatedUser property set to nil since it has a weak reference. User object is now free it’s all strong reference holding property will also nil which decreases the reference count of Todo object as well resulting it to 1:

Figure 13

todo = nil
Setting todo to nil will make it reference count to 0 and ARC will free Todo object from memory. We solved Strong reference cycle using weak reference which will create our rule number 1.
Rule 1 :
When two objects has a bidirectional relationship make one of them weak or unowned.
Memory Leaks in Closure
Rule of Thumb
Memory Leak in Closure = self refers to → object refers to → self

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closure captures variables and constants from its surrounding scope.
Capturing ?
As said above Closure captures variables and constants from its surrounding scope. Let’s see in action. We performed a number of steps in Figure 14:

  1. First, we define two local variables a and b with values 20 and 30 respectively. When we define any variable in a method it can only be accessed within the scope.
  2. Second, we define someClosure variable that captures a and b by strong reference to it to prevent those values from being released, and the closure from crashing in case they were deallocated.
  3. When someMethodThatTakeClosure method call it will execute the closure and closure will return the sum of a and b which were captured on viewDidLoad. As shown in Figure 14 sum value that was printed 50.

Figure 14

Section 2
Memory Leaks in Closure
When we create a closure the value that it needs for its execution it holds a strong reference to it what if the value needed by the closure is self and closure is a property of the controller.

As shown in Figure 15 we created an optional closure as a property of the controller with type ( ) -> Int and assign value to it in viewDidLoad. Closure needs a and b properties to execute it. Also since a and b are the properties of a class it will have to capture class reference as well by capturing self. These are the only external values it needs, and to keep those values available.

Figure 15

var someClosure: (() -> Int)?
First, we are saying that self.someClosure store a strong reference to the closure:
someClosure = { return self.a + self.b }
Since ViewController and Closure both are reference type ARC reference counting system will apply on both.

By executing the above statement we define closure and assign it to the class property which holds a strong reference to closure and increment the reference count of closure to 1 as shown in Figure 16.

Since Controller was pushed on the navigation controller. Navigation controller create a strong reference to ViewController which increment the Reference count to 1 of the controller which also prevents ViewController from dealloc.

At the same time since closure needs a and b property it holds/captures the class reference as well which makes the reference count of the class by 2.

Figure 16

When you pop ViewController it removed the controller from the navigation stack which decreases its reference count to 1.

Ideally, ViewController deinit should call but we created a retain cycle. This occurs when we make circular references between two or more objects as shown in Figure 17.

ARC looks and not found any reference count 0 so it will not remove both from memory:

Figure 17

Solution
Make one of the ones references as weak/unowned and the other as strong, therefore, the circular reference is broken.

As shown in Figure 18 closure now capturing a weak reference to self. since we are using weak which makes self as optional that’s why we use a guard to safe unwrap the value of self:

Figure 18

var someClosure: (() -> Int)?
self.someClosure = { [weak self] in guard let `self` = self else { return 0 return self.a + self.b}
As shown in Figure 19 Navigation controller create a strong reference to SecondViewController which makes it reference count to 1 and SecondViewController is owning closure since closure is a class property which makes the reference count of closure to 1. But now closure weakly capture self as we defined in capture list which will not increment the reference count of closure as shown below:

Figure 19

Now after popViewController following action will perform:

  1. The navigation controller removed the ViewController from stack and decrement the reference count to 0.
  2. ARC looks zero reference count instance and found ViewController instance then removed it from memory and also removed their associated ownerships.
  3. Since ViewController strong and weak associated reference also removed which decrement the reference count of closure to 0 and ultimately removed it from memory.
Difference Between weak and unowned:
Unowned
Like a weak reference, an unowned reference does not keep a stronghold on the instance it refers to (means it can use to break the retain cycle).

Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime.

Let’s put in our case which means if you are sure that when closure executes self is available otherwise the application will crash. OR self has a longer lifetime then closure.

Download the starter project

Run application >> it will display screen having button >> Tap on Button it will Push SecondViewController >>

On ViewDidLoad It executes someMethodThatTakeClosure which takes 4 seconds. After 4 seconds it will execute closure which requires self so before 4 seconds if we pop secondviewController application will crash as shown in Figure 20 since in this case closure have a longer lifetime and Second ViewController also deallocated:

Figure 20

Fix
Use weak self as shown in Figure 21 which makes self optional and using guard we can safely unwrap self. If the self is already deallocated we can handle properly:

Figure 21

Rule of Thumb
Use weak if you are not sure your capture self in a closure may or may not available in some cases.

Use unowned if you are 100% sure that self will be available when you're closure executes.
Rule 2:
When you have class level closure and inside closure, if you are accessing anything using self make self weak /unowned.
No Memory Leak Case 1
Local method closure capturing self will not create any memory leaks

As shown in Figure 22 we created closure in a local scope of someMethod since closure is not a class property of a controller, the controller doesn't hold a strong reference to closure but closure captures self strongly as shown in Figure 22 which means when closure execute controller will deallocate. In this case, closure always has a longer lifetime ????

Figure 22

As shown in Figure 23:

  1. The navigation controller holds a strong reference to SecondViewController makes it reference count to 1.
  2. Closure captures self strongly makes SecondViewController instance RF = 2.
  3. newSomeClosure variable holds a strong reference to a closure that makes RF = 1.

Figure 23

When you popviewController following things happen as shown in Gif 1:

  1. SecondViewController RF = 1
  2. When the closure executes and local scope which holding it strongly removed from the stack. Closure RF will decrease by 1 which make it 0
  3. Closure RF = 0 removed all ownership from him and make the SecondViewController RF = 0 which ultimately rehomed ViewController from memory

Gif 1

No Memory Leak Case 2
The static method that captures the self does not create any memory leaks.

As shown in Figure 24 self is not owning static class but static class closure capturing self strongly.

Static Class → Closure → Self (Will not create memory leaks)

Figure 24

The code below will not leak any memory since Class not owning DispatchQueue.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {

self.execute( )

}
Memory Leak
Rule of Thumb:
If you are accessing Singleton / Static class that holds closure in their class The time in which it holds the closure memory will leaks in this time duration.

As shown in Figure 25 we have a singleton class that holds closure in someSingletonMethod and after executed closure, that method scope will end and closure will free from memory since local variable holding the closure strongly is removed:
SingletonClass.shared.someSingletonMethod(self.a) { (value) in

self.execute( )

}

Figure 25

As shown in Figure 26 we have a singleton class that holds closure in someSingletonMemoryLeakMethod and when executing it holds a strong reference of that closure in class level property and after executed closure, it will not release it since Singleton class always remain in memory it will hold the closure which eventually created memory leak. In this case, a memory leak will occur.
Memory Leak = SingletonClass → Closure → Self
Until SingletonClass remain in Memory, self (ViewController) will remain in memory.

When we do pop controller will not deallocate since closure still holding it strongly which is stored or not released by Singleton Class:
SingletonClass.shared.someSingletonMemoryLeakMethod(self.a) { (value) in

self.execute( )

}

Figure 26

Solution
Use a weak self to avoid memory leak:

No Memory Leak = SingletonClass → Closure

Now SingletonClass only holds closure reference since closure is using weak self When we do pop controller it will deallocate since it has only navigation controller owner.

This scenario same goes to static class:
Identify Memory Leaks Using Memory Graph Debugger
we know the fix in this part we will only identify memory leaks and fix by using previous techniques. The main purpose is to use Memory Graph Debugger to identify leaks in an existing project. Download the starter project.
Memory Graph Debugger
In one sentence, the memory graph debugger helps to answer the following question: Why does an object exist in memory?

The Xcode memory graph debugger helps find and fix retain cycles and leaked memory. When activated, it pauses app execution, and displays objects currently on the heap, along with their relationships and what references are keeping them alive.
Open starter project and run application tap on Button will redirect to a new screen. Tap until you reach the screen having title “Third View Controller Title”, Now tap back a to pop Third View Controller will display “Second View Controller”. After tapping back you need to see whether the memory is leaking in Third View Controller or not.

As shown in Figure 27 we are now in SecondViewController but our ThirdViewController still in memory which means it was not deallocated. By tap on this, we identify there is some closure holding it strongly:

Figure 27

We found in ViewDidLoad as shown in Figure 28 and fix it in Figure 29:

Figure 28

Figure 29

Now again run the application and follow the same step there will be no memory leaks. As shown there is no ThirdViewController in the heap which means it was deallocated:

Figure 30

We are happy that ???? we removed all the memory leaks but still, there are some memory leaks when some events occur in this controller.

Run application again go to the ThirdViewController and tap on Button 1. On tapping this will create memory leaks since inside its action as shown in Figure 31 memory leak is happening:

Figure 31

In short:
To find memory leaks you need to perform every flows that your controller will perform and check in-memory graph debugger heaps or print something in deinit.
Useful Links:
Источник: medium.com