Tutorials

Memory Management in Swift

Understanding Memory Management, Strong, Weak and Unowned References

Behind all the coding that we are doing, you may have noticed some of your variables with the reference of strong, weak or unowned when writing your codes. What is Memory Management and what does the reference really means?

The usage of strong, weak or unowned are actually related to the memory management in Swift called Automatic Reference Counting also referred to as (ARC). Let’s slow down a little and try to understand what this means. So, ARC actually does automatic reference counting. In the definition of Computer Science, Reference Counting is a technique of storing number of references, pointers, or handles into a resources such as an object, block or memory, disk space or other resources. In short, ARC actually helps store references into memory and helps clean up when it is not being used.

On a side note, reference counting in this case only applies to instance of classes and not structures and enumerations as they are both value types and not reference types.

Before we go further, why is memory management such a big deal? It is indeed a big deal as memory management plays a huge role in allocating memory so that the program can perform at the request of the user and could be free for reuse when no longer needed.

But what could happen if you exhaust the memory provided to your App?

  • The task will stop performing meaning you won’t be able to execute any of your task.
  • The task probably will not progress but continue running and running until it hits the limit and the program crashed.
  • You probably don’t want the user to use a buggy program.
  • Your app may be killed by iOS if it runs out of memory

What is Automatic Reference Counting (ARC)?

As Apple Documentation point it out:

Memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

ARC also keeps track of information such as knowing the relationship between the code and because of that, ARC is able to manage the memory resource effectively.

Now yo may ask How does ARC work?

Each time we create a class instance through init(), ARC automatically allocates some memory to store the information. To be more specific, that chunk of memory holds the instance, together with the values of the properties. When the instance is no longer needed, deinit() will be called and ARC will free the memory space of that instance.

With the example below, it is pretty self explanatory but I still want you to understand what the code does. This is an example of two classes with User instance and Device instance which has an init method where it will set the instance’s property meaning allocate whatever information into the memory. Also, with deinit() where we will see the instance being deallocated meaning memory containing the information will free up in our case.

class User{
    let name: String
    init(name: String) {
        self.name = name
        print(\(name) is being initialized”)
    }
    var device: Device?
    deinit {
        print(\(name) is being deinitialized”)
    }
}
class Device {
    let model: String
    init(model: String) {
        self.model = model
        print(\(model) is being initialized”)
    }
    var owner: User?
    deinit {
        print(\(model) is being deinitialized”)
    }
}

This is an example of a class called User and the instance has a name property with a type of String. The User class has an init() where it will set the instance’s name property meaning we will allocate whatever information into the memory. In the following code, there is a deinit() where we will see the instance being deallocated meaning memory containing the information will free up. And, the User instance has a device property of type String and an optional Device because a user may not have a device. And the same theory applies with the Device class.

The Difference between Strong, Weak and Unowned Reference

Strong vs Weak vs Unowned

  • Usually, when a property is being created, the reference is strong unless they are declared weak or unowned.
  • With the property labelled as weak, it will not increment the reference count
  • An unowned reference falls in between, they are neither strong nor or type optional. Compiler will assume that object is not deallocated as the reference itself remain allocated.

Strong reference

Let’s take a look at the following example. We have a variable of User type with the reference of “Bob” and a variable of Device type with the reference of “iPhone 8”.

var bob: User?
var iphone: Device?

bob= User(name: “Bob”)
iphone = Device(model: “iPhone 8 “)

 

Now if you set both variables to nil and run the code in Playground

bob = nil

iphone = nil

You should see both variables are deinitialized properly.

Now add the following code to assign bob with an iphone and set the owner of the iphone. Make sure you insert the code before setting bob and iphone to nil:

bob!.device= iphone
iphone!.owner = bob
Remember that in the User class that there is an additional variable of device, here we are giving it a value of iphone variable.
Lets run the code again in Playgrounds. You should see that the console only shows you messages related to initialization.

When we break the strong reference, the reference count did not drop to zero and the instances were not deallocated by ARC. Why? The strong reference between the User instance and the Device instance remain and cannot be broken even both Bob and iphone were set to nil.

This is what we call strong reference cycle, leading to memory leaks in your apps. To break the strong reference cycle and prevent memory leaks, you will need to use weak and unowned references.

Weak reference

Weak reference are always declared as optional types because the value of the variable can be set to nil. In order to indicate a reference as weak, you simply add weak keyword before the property or variable declaration like this:

weak var owner: User?

ARC automatically sets weak reference to nil when the instance is deallocated. And because of the change of value, we know that variable will need to be used here as constants will not let you change the value.

We can break the strong reference cycle by using weak references. A weak reference does not keep a strong hold on the instance. With the weak reference, we can resolve the memory leak issue in the previous section. We will modify the Device class and declare owner as weak reference like this:

class Device{
    let model: String
    init(model: String) {
        self.model = model
        print(\(model) is being initialized”)
    }
    weak var owner: User?
    deinit {
        print(\(model) is being deinitialized”)
    }
}
Now run the code in Playgrounds and see that the values are deinitialized as they should. After changing the owner variable to weak, the relationship between two instances looks a little different.
With the weak reference, when you set bob to nil, the variable can be deallocated properly because there is no more strong reference pointing to the User instance.

Conclusion

I hope you now have a better understanding of strong, weak, and unowned references. Xcode provides developers with built-in facilities to debug memory issues. For example, if you want to detect memory leaks, you can go up to the menu and select Product > Profile > Leaks.

Look at our iOS 12 course for beginners for more info.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.