Memory Management in Swift by ARC

Avoid Memory leaks and app crashes

Karthikeyan T
IVYMobility TechBytes
9 min readJun 4, 2020

--

Memory Management is the programming discipline of managing the object’s life cycles and releasing them when it is not needed. Managing object memory is key for the app's performance i.e., if an app doesn’t free unneeded objects, its performance will be compromised.

You’ll level up your Memory Management skills with the following topics:

  • ARC — explained in a short description.
  • How ARC is working.? with an example.
  • What is a Strong Reference Cycle.?
  • Break the cycle by weak and unowned references with an example.
  • Choosing weak and unowned references to break the cycle.
  • Strong Reference Cycles in Closures.

ARC — Automatic Reference Counting

  • ARC is a compiler feature that provides automatic memory management of objects.
  • Before ARC, a developer needs to deallocate the objects which are no longer needed by inserting the code wherever needs.
  • ARC works by adding code at compile time to ensure that objects live as long as necessary, but no longer. That is it will start counting the number of references to each object. When the reference count drops to zero then the object memory is released.
  • ARC will keep track of objects and it automatically releases the objects which are no longer needed.
  • ARC will work only for the reference type i.e., class and not for value types i.e., struct and Enum The reason is that in reference type only there may be a situation where multiple objects may refer to the same object.

As a develpoer, we don’t need to care about object management, but you need to know the relationships between objects in your application.

ARC — How it works?

  • ARC allocates a memory every time you create a new instance of a class which includes the details about the type of the instance and the stored property values associated with the instance.
  • Since ARC holds the information about the object, it can able to free its memory. But if the object is released which is still in use, then the application may end up with a crash if you tried to access the instance.
  • To overcome this scenario, ARC tracks the properties that are currently referred to in all the instances of the class. So it will not release the memory of the object until the existence of at least one active reference to the instance.
  • To make this solution as possible one, we need to assign a class instance to any property as a strong reference to the instance. Therefore, ARC holds the instance so tightly and it won’t deallocate the instance until the strong reference remains.
  • Special care should be taken to achieve the above-mentioned solution. Because in some cases it will lead to Strong Reference Cycles between the class instance. If the cycle is formed then the ARC will not work.

Let us see how ARC works with an example. GitHub

The User class has an initializer method that sets the values for name and idproperties and also prints a message to indicate that initialization has been done. The User class also has a deinitializer that prints a message when all class instances are deallocated.

The following code snippet defines three variables of type User?, used to set up multiple references to a new User instance in subsequent code. Next, create a new User instance and assign it to one of the instances say user1.

Now in the Debug view, "User kevin with an ID kevin.r@gmail.com is being initailized" is printed confirming that initialization has taken place. To confirm the same declare two more instances of type User? and initialize these two instances with user1

We need to assign nil to all three objects to deinitialize the User class instance.

From the above example, it is clear that the ARC won’t deallocate the User instance until all three instances are broken.

Now, add a CoreFoundation function CFGetRetainCount(_:) to showcase the role of reference count in ARC.

Strong Reference Cycle

Even though ARC will take care of object management, we need to take care of the relationship between objects. If you cannot map the relations between the objects properly, then in some cases, an instance of a class never gets deinitialized. This will happen if two class instances hold a strong reference to each other and this scenario is termed a Strong Reference Cycle.

In the upcoming example, two classes called Person and Application which models the group of Applications signed up the list of Users will be defined in such a way as to create a strong reference cycle.

User class with a strong app object
Application class with a strong user object

Next, add the following line and run in your playground.

So far so good as both the objects are initialized and deinitialized (as the object will survive only within the scope) so then the memory is recycled.

Now, add musicApplication to kevinTheUser.app and kevinTheUser to musicApplication.user.

Swift code to show the Strong Reference Cycle

Now run the code, and you’ll see in the debug console that only kevinTheUser and musicApplication do not deallocate as expected. Because a strong reference cycle has been formed between these two objects (as shown in the image below) that prevents ARC to do his job.

Visual representation of the relation between the objects results in Strong Reference Cycle

Breaking this Strong Reference Cycle can be done by specifying the relationship between reference objects by weak or unowned references. Because it won’t increase the reference count of an object.

  • Now, we need to do some changes to break the cycle by specifying the user property in the Application class as weak
Application class with a weak user object

So the Strong Reference Cycle will be broken as shown in the below figure.

Visual representation of the relation between the objects after breaking the cycle

Now, the ARC will work as expected.

Swift code to show the breaking of Strong Reference Cycle
  • Next with another example, we can see how unowned contributed to breaking the Strong Reference Cycle. Now consider another class called Subcription which holds information like name, id, isActive, and user details.
Subscription class with a Strong user object
Visual representation of the relation between the objects results in Strong Reference Cycle
Swift code to show the Strong Reference Cycle

In the above example, it is clearly inferred that a Strong Reference Cycle is formed as shown in the above visual Representation. Now, make the user as unowned in Subscription class to break the cycle as shown in the below code and the same has been visually represented.

Subscription class with an unowned user object
Visual representation of the relation between the objects after breaking the cycle
Visual representation of the relation between the objects after breaking the cycle
Swift code to show the breaking the Strong Reference Cycle

Since the Strong Reference Cycle has been broken, both the objects get deallocated once it is out of scope as shown in the below code.

Choosing weak and unowned references to break the cycle.

Weak reference when the other instance has a shorter lifetime and it should be declared as variables of an optional type and it gets automatically becomes nil when the referenced object goes away. In simple words, weak can be used where two properties, both of which are allowed to be nil. In the above example, for an Application class, it is not mandatory to get the User details, because signup is not mandatory for all applications.

In simple words, weak can be used where two properties, both of which are allowed to be nil. In the above example, for an Application class, it is not mandatory to get the User details, because signup is not mandatory for all applications.

Unowned reference is used when the other instance has the same lifetime or a longer lifetime and it will accept only non-optional type.

In simple words, unowned used where one property is allowed to be nil and another property cannot be nil which has the potential to cause a strong reference cycle. In the above example, the Subscription class has the property user of type User should be a non-optional value. Because the subscription should know the details of the user.

The use of unowned to break the cycle may cause a problem during run time. For example, If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error.

So far, it is cleared about the fundamental concepts of ARC such as how it forms by strong relations between objects and how to resolve Reference Cycles using weak and unowned. In the next section, we going to see how the Strong Reference Cycle is formed inClosures and of course how to break the same cycle.

Strong Reference Cycles in Closures

In Closure, a Strong Reference Cycle will occur if the closure is assigned to a property of a class instance, and the body of that closure captures the instance i.e., class instance and closure are keeping each other alive. For example, if the closure’s body uses self.someProperty or self.someFunction() then it leads to a Strong Reference Cycle.

The below example shows how a strong reference cycle is created when using a closure referenced toself. This example defines a class called User In addition to these two simple properties (name & id), the User class defines a lazy property called detailsof type () -> String. This property references a closure that combines name and id into a sing String.

In the above example, the User class creates a strong reference cycle between an User instance and the closure details. Here’s what the cycle looks like:

Visual representation of how Strong Reference Cycle between Class instance and closure
Swift code to show the Strong Reference Cycle

A strong Reference Cycle has been resolved between the class instance and closure by defining a capture list in the closure’s definition e.g. [unowned self] or [weak self]. as mentioned below.

Breaking the Strong Reference Cycle by included [unowned self] in the capture list
Breaking the Strong Reference Cycle by included [weak self] in the capture list
Visual representation of breaking the cycle using unowned in the Closure’s capture list

So the User class is now redefined to eliminate the Strong Reference Cycle.

User class after eliminating the Strong Reference Cycle

Now run to the playground to witness the breaking of the Strong Reference Cycle

Swift code to show the breaking of Strong Reference Cycle

Source: Apple Docs and sources from Internet

Get the full source code at GitHub

Reach me on LinkedIn

--

--