Retain cycles and weak references

Because classes in Swift rely on reference counting to remove them from memory, it’s important to understand the concept of a retain cycle.

Imagine a modified version of Student that has a field representing a classmate — for example, a lab partner:

class Student: Person { var partner: Student?

deinit {

print("(firstName) being deallocated!")

}

}

var john: Student? = Student(firstName: "Johnny", lastName: "Appleseed") var jane: Student? = Student(firstName: "Jane", lastName: "Appleseed")

john?.partner = jane jane?.partner = john

Now suppose both jane and john drop out of school:

john = nil jane = nil

If you run this in your playground, you’ll notice that you don’t see the message

Johnny/Jane being deallocated!, and Swift doesn’t call deinit. Why is that?

John and Jane each have a reference to each other, so the reference count never reaches zero! To make things worse, by assigning nil to john and jane, there are no more references to the initial objects. This is a classic case of a retain cycle, which leads to a software bug known as a memory leak.

With a memory leak, memory isn’t freed up even though its practical lifecycle has ended. Retain cycles are the most common cause of memory leaks.

Fortunately, there’s a way that the Student object can reference another Student

without being prone to retain cycles, and that’s by making the reference weak:

class Student: Person { weak var partner: Student?

//…

}

This simple modification marks the partner variable as weak, which means the reference in this variable will not take part in reference counting. When a reference isn’t weak, it’s called a strong reference, which is the default in Swift. Weak references must be declared as optional types so that when the object that they are referencing is released, it automatically becomes nil.

results matching ""

    No results matching ""