Strong vs Weak vs Unowned References in Swift
Swift relies on Automatic Reference Counting (ARC) for managing the lifecycle of objects by freeing up memory for objects when there are zero strong references to them. However, a strong reference cycle can happen when two objects hold a strong reference to each other.
A strong reference protects the object from getting deallocated by ARC, which increases its retain count by 1.
A reference to a class instance is strong by default. Basically, as long as anything has a strong reference to an object, it will not be deallocated. It is ok to use a strong reference when the flow is from parent to child, as you can see in the example below.
Both instrument and player deinit methods are invoked when we set the objects to nil.
STRONG REFERENCE CYCLE - MEMORY LEAKS
We now add a players array to the Instrument instance (line 3) and when we initialise a Player instance (line 5), we add it to the instrument’s players array (line 22).
The instrument and player deinit methods are not invoked when we set the instances to nil.
When we set the instrument instance to nil, the ARC checks if there is an instance that keeps a strong reference to it, and since there is a player instance with a strong reference to it, the object won’t be released (strong reference cycle), which is leading us to what we call a memory leak.
A weak reference does not protect the object from getting deallocated by ARC (which does not increase its retain count by 1) and is always optional.
A weak reference is just a pointer to an object that doesn’t protect the object from being deallocated by ARC. The reference will be mutated to nil when there is no longer anything holding a strong reference to it.
We can use a weak reference to avoid strong reference cycles and memory leaks (line 17):
When the instrument is set to nil, the only instance that has a reference to it is the player one, and because it’s a weak reference, ARC releases the Instrument instance.
A unowned reference is similar to a weak reference (ARC does not increase its retain count by 1) but has the added benefit to be non-optional.
Unowned references are non-zeroing. This means that when the object is deallocated, it doesn’t zero out the pointer. As we can see in the example below, changing the weak reference (line 17) to an unowned one, has the same results as before.
According to Apple’s docs:
Use an unowned reference only when you are sure that the reference always refers to an instance that has not been deallocated.
If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error.