Strong, weak and unowned reference types

Janvi Arora
4 min readMar 9, 2023

--

A strong reference cycle, also known as a retain cycle, occurs in programming languages that use automatic memory management, such as Swift, Objective-C, and Java, when objects hold strong references to each other in a circular manner, causing them to become ineligible for garbage collection. This means that the objects will remain in memory indefinitely, even if they are no longer needed, resulting in a memory leak.

To understand this concept better, let’s consider an example in Swift:

class Person {
var name: String
var car: Car?

init(name: String) {
self.name = name
print("\(name) is being initialized")
}

deinit {
print("\(name) is being deallocated")
}
}

class Car {
var model: String
var owner: Person?

init(model: String) {
self.model = model
print("\(model) is being initialized")
}

deinit {
print("\(model) is being deallocated")
}
}

var john: Person? = Person(name: "John")
var tesla: Car? = Car(model: "Tesla")

john?.car = tesla
tesla?.owner = john

john = nil
tesla = nil

In this example, we have two classes, Person and Car, each of which has a property that holds a strong reference to an instance of the other class. When we create instances of these classes and assign them to variables john and tesla, respectively, we also set their car and owner properties to each other, creating a circular strong reference.

Now, when we set both john and tesla to nil, we might expect both instances to be deallocated and their deinit methods to be called. However, since they hold strong references to each other, neither instance can be deallocated, resulting in a memory leak.

To avoid strong reference cycles, we can use weak or unowned references instead of strong references. A weak reference allows an object to hold a reference to another object without increasing its reference count, so if the referenced object is deallocated, the weak reference becomes nil. An unowned reference is similar to a weak reference, but it assumes that the referenced object will never be nil, so it does not need to be optional.

Here’s how we can modify the previous example to avoid the strong reference cycle using weak references:

class Person {
var name: String
weak var car: Car?

init(name: String) {
self.name = name
print("\(name) is being initialized")
}

deinit {
print("\(name) is being deallocated")
}
}

class Car {
var model: String
unowned var owner: Person

init(model: String, owner: Person) {
self.model = model
self.owner = owner
print("\(model) is being initialized")
}

deinit {
print("\(model) is being deallocated")
}
}

var john: Person? = Person(name: "John")
var tesla: Car? = Car(model: "Tesla", owner: john!)

john?.car = tesla

john = nil
tesla = nil

In this modified example, we’ve changed the car property of Person to a weak reference, so it does not hold a strong reference to the Car instance. Instead, we've added an owner parameter to the Car initializer, which is an unowned reference to the Person instance that owns the car. This ensures that the Person instance will always exist as long as the Car

Main difference between weak and unowned reference cycles:

In Swift, both weak and unowned are used to avoid strong reference cycles, but they have different use cases and behaviours.

A weak reference allows an object to hold a reference to another object without increasing its reference count, and if the referenced object is deallocated, the weak reference becomes nil. This means that a weak reference is always optional, and you need to check if it's nil before using it.

Here’s an example of using a weak reference:

class Person {
var name: String
weak var car: Car?

init(name: String) {
self.name = name
}

deinit {
print("\(name) is being deallocated")
}
}

class Car {
var model: String

init(model: String) {
self.model = model
}

deinit {
print("\(model) is being deallocated")
}
}

var john: Person? = Person(name: "John")
var tesla: Car? = Car(model: "Tesla")

john?.car = tesla
tesla = nil

print(john?.car) // prints "nil"

“weak” and “unowned” are used to avoid retain cycles, which occur when two or more objects hold strong references to each other, preventing them from being deallocated by the memory management system.

A “weak” reference is a reference to an object that does not prevent the object from being deallocated. If the object being referenced by the weak reference is deallocated, the weak reference will automatically be set to nil. Weak references must always be declared as optional.

An “unowned” reference is also a reference to an object that does not prevent the object from being deallocated. However, unlike weak references, unowned references are assumed to always have a value, and they are not optional. If an unowned reference is used after the object being referenced has been deallocated, the application will crash.

To summarize, “weak” references are optional and automatically set to nil when the referenced object is deallocated, while “unowned” references are non-optional and assume that the referenced object always exists, which can lead to a crash if that assumption is incorrect. Therefore, it’s important to use “weak” and “unowned” references appropriately based on the specific requirements of your code.

--

--

No responses yet