Swift ARC and Weak References
Automatic Reference Counting tracks how many strong references keep a class object alive. An object is removed only when that count falls to zero.
A strong cycle leaks
Ana and Ben hold each other through strong friend properties. Both outside variables are set to nil, yet no deinit line prints before the closing line.
class Person {
var name: String
var friend: Person?
init(name: String) {
self.name = name
}
deinit {
print("\(name) gone")
}
}
var ana: Person? = Person(name: "Ana")
var ben: Person? = Person(name: "Ben")
ana?.friend = ben
ben?.friend = ana
ana = nil
ben = nil
print("Done")One weak side breaks the cycle
The assistant holds the teacher through a weak var, while the teacher holds the assistant strongly. The teacher is set to nil first and is removed first.
class Teacher {
var name: String
var assistant: Assistant?
init(name: String) {
self.name = name
}
deinit {
print("Teacher gone")
}
}
class Assistant {
var name: String
weak var teacher: Teacher?
init(name: String) {
self.name = name
}
deinit {
print("Assistant gone")
}
}
var teacher: Teacher? = Teacher(name: "Rao")
var assistant: Assistant? = Assistant(name: "Kim")
teacher?.assistant = assistant
assistant?.teacher = teacher
teacher = nil
assistant = nil
print("Done")Reading a weak reference after removal
The profile reads the author name through an optional chain while the author lives. After the author variable is set to nil, the same read falls back to a default line.
class Author {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("Author gone")
}
}
class Profile {
weak var author: Author?
}
let profile = Profile()
var author: Author? = Author(name: "Mina")
profile.author = author
print(profile.author?.name ?? "No author")
author = nil
print(profile.author?.name ?? "No author")Example
class Library {
var member: Member?
deinit {
print("Library gone")
}
}
class Member {
var name: String
weak var library: Library?
init(name: String) {
self.name = name
}
deinit {
print("Member gone")
}
}
var library: Library? = Library()
var member: Member? = Member(name: "Ana")
library?.member = member
member?.library = library
library = nil
member = nil
print("Done")Key points
- Each strong reference raises the count of its object by one.
- Two objects that hold each other strongly form a reference cycle.
- A cycle keeps both counts above zero after the outside variables are set to nil.
- A weak reference does not raise the count of its object.
- A weak reference is declared with weak var and an optional type.
- Swift sets a weak reference to nil after its object is removed.
In this example, the Library and Member classes hold each other, and the member side holds a weak reference back. Setting the library variable to nil removes the library first. Setting the member variable to nil then removes the member, so both gone lines print before Done.
Note
Making one side of a pair weak is enough to break a cycle between them.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Two objects hold each other, and both outside variables are set to nil, yet neither object is removed. What is the most likely cause?