What is the difference between a weak reference and an unowned reference?


Understanding iOS memory management is essential for iOS and macOS development. The concept of weak self and unowned self is challenging to understand, especially for beginners. ARC (Automatic Reference Counting) may have solved many problems for us. Swift still requires that you manage references a lot of the time when you are not working with value types.

ARC or Automatic Reference Counting

Automatic Reference Counting (ARC) is used for tracking and managing the app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you don’t need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

Note that by default in Swift, a reference is counted as strong.

import UIKit
class ProductListController: UIViewController {
    
   // Create a strong reference to a ProductListDataSource() instance. When the ProductListController instance is deallocated, it will decrease the reference count of the ProductListDataSource instance by 1.
   let dataSource = ProductListDataSource()
    
   override func viewDidLoad() {
      super.viewDidLoad()
   }
}

In the above example, the ProductListController class has a strong reference of the ProductListDataSource() type.

An object’s reference count is increased by 1 when a strong reference is assigned to that object. An object’s reference count is decreased by 1 when a strong reference is removed from that object.

What does it mean to have a strong reference?

Let's start by understanding what a strong reference is. It's essentially a default reference (pointer and all), but it's special in its own right in that it protects the referred object from getting deallocated by ARC by increasing its retain count by one. In essence, as long as anything has a strong reference to an object, it will not be deallocated. This is something to remember later when I explain retain cycles and stuff.

The Swift codebase nearly exclusively makes use of strong references. All property declarations are strong by default. In general, when object hierarchy relationships are linear, it is permissible to use strong references. It is usually wise to rely on strong references when a hierarchy of them runs from parent to child.

UIView.animate(withDuration: 0.5) {
   self.view.alpha = 0.5
}

Since animateWithDuration is a static method on UIView, the closure here is the parent and the self is the child.

Weak References

Weak references are one of the solutions to the retain cycle problem in Swift. Note that a weak reference does not increment or decrement the reference count of an object. They can be deallocated even if ARC has a reference count greater than 1.

Basically, we use the weak keyword in Swift to mark a reference as weak. Also, a weak reference cannot be declared with the let keyword since it will at some point be passed to nil. This is because the object could be deallocated while the weak reference is pointing to it. There should be a weak reference in the code where you think the retain cycle might be generated.

How to fix the retain cycle using weak reference

We will see an example of how we can fix the retain cycle. We will create a data source model class to fetch initial data using a network request.

import UIKit
class ProductListDataSource {
    
   func loadInitialData(_ completion: ((_ isSuccess: Bool) -> ())?) {
      // assume perform networking request to fetch initial data
      // returning completion handler after finishing the task
      completion?(true)
   }
}
class ProductListController: UIViewController {
    
   // Created a strong reference to a ProductListDataSource() instance. When the ProductListController instance is deallocated, it will decrease the reference count of the ProductListDataSource instance by 1.
   let dataSource = ProductListDataSource()
    
   override func viewDidLoad() {
      super.viewDidLoad()
        
      /*
      1. Calling the loadInitialData function no longer creates a retain cycle.
      2. Self (ProductListController) has a strong reference to the data source.
      3. Completion handler has a weak reference to self (ProductListController).
      */
      dataSource.loadInitialData { [weak self] isSuccess in
         guard let self = self else { return }
            
         // perform tasks that need to be done after successfully fetching data
      }
   }
}

In the above example, you can see we used the weak reference of self (ProductListController()) as it might be required to perform certain actions once the data is fetched from the server.

Unowned References in Swift

An unowned reference is very similar to a weak reference, but with one difference. An unowned reference will give you a guarantee that the variable will not be nil when it is accessed.

Similar to a weak reference, an unowned reference does not increment or decrease an object's reference count. We can say it is another solution to fix the retain cycle. Accessing an unowned reference when the instance the reference points to is nil will cause a fatal program error.

What difference between both of them?

A weak reference is a reference that does not prevent the referenced object from being deallocated by the Swift runtime.

An unowned reference, on the other hand, is a reference that does not keep a strong hold on the referenced object. Unlike a weak reference, however, an unowned reference is assumed to always have a value.

Summarize the differences between both

Difference between weak and unowned references

Weak Reference Unowned Reference
It can be declared optional. It cannot be declared optional.
It can become nil at any time. It cannot become nil at any time.
It prevents objects to become deallocated. It does not prevent objects to become deallocated.
Fewer chances to occur runtime errors if accessing the deallocated reference object. High chance to occur runtime error if accessing the deallocated reference object.

Which reference is appropriate to use?

Use a Weak Reference

When there is a relationship between two objects where one should not have a strong grip on the other, you should utilize a weak reference to stop a retain loop. When two objects are strongly referenced by one another, a retain cycle results, prohibiting the Swift runtime from deallocating either object.

For instance, you would normally use a weak reference for the kid's reference to the parent when building a parent-child relationship between two objects to avoid the parent from being maintained permanently by the child.

Use an Unowned Reference

When there is a relationship between two objects in which one does not have a strong hold over the other but you are confident that the referred object will always exist over the lifetime of the referencing object, you should use an unowned reference.

For instance, you may use an unowned reference for the view controller's reference to a view if the view controller constantly presents the same view, since the view will always exist as long as the view controller does.

Conclusion

In conclusion, both references play an important role in their own situation. But most of the time, we use weak references in the application. This is because weak references are better in the case of memory management. As weak references can be declared optional and handle nil, it is recommended to use them.

Updated on: 24-Mar-2023

4K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements