Memory Management in Swift by ARC
Avoid Memory leaks and app crashes
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
andEnum
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 id
properties 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.
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.
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.
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 theApplication
class asweak
So the Strong Reference Cycle will be broken as shown in the below figure.
Now, the ARC will work as expected.
- Next with another example, we can see how
unowned
contributed to breaking the Strong Reference Cycle. Now consider another class calledSubcription
which holds information like name, id, isActive, and user details.
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.
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 details
of 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:
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.
So the User class is now redefined to eliminate the Strong Reference Cycle.
Now run to the playground to witness the breaking of the Strong Reference Cycle
Source: Apple Docs and sources from Internet
Get the full source code at GitHub
Reach me on LinkedIn