Null safety and memory, 3 of 5
Weak references
Break reference cycles with Weak<T> and [Weak Self], so objects that point at each other are freed.
What you’ll build
A folder tree in which each folder knows its parent and its children, and a button whose click handler calls back into the object that owns it. Both make objects that refer to each other, and both use weak references so that they’re still freed.
A cycle never reaches zero
When two objects refer to each other, each keeps the other’s count above zero, so neither is ever
freed, even once nothing else refers to them. That’s a leak. A debug build reports it when the
program ends, as here, where Destroy never runs for either node.
Import std.core
Class Node
ReadOnly Field Name: String
Field Next: Node?
New(name: String)
Name = name
End New
Method Destroy()
Print("free {Name}")
End Method
End Class
Local a = Node("a")
Local b = Node("b")
a.Next = b
b.Next = a
Print("done")done leak: 2 Node objects were never released leak: a cycle of 2 objects: Node.Next -> Node.Next -> the first Node
Weak
A Weak<Folder> field refers to a Folder without counting as a reference to it. It’s assigned a
Folder as usual, and reading it gives a Folder?, which is Null once the Folder has been freed.
A tree’s children hold strong references, and each child’s link to its parent is weak, so the
tree has no cycle.
Import std.core
Class Folder
ReadOnly Field Name: String
Field Parent: Weak<Folder>
Field Children: Folder[] = []
New(name: String)
Name = name
End New
Method Add(child: Folder)
Children.Add(child)
child.Parent = Self
End Method
Method Path(): String
Local parent = Parent
If parent == Null Then Return Name
Return parent.Path() + "/" + Name
End Method
Method Destroy()
Print("free {Name}")
End Method
End Class
Local home = Folder("home")
Local docs = Folder("docs")
home.Add(docs)
Local notes = Folder("notes")
docs.Add(notes)
Print(notes.Path())
home = Folder("elsewhere")
Print("docs' parent: {docs.Parent?.Name ?? "gone"}")
Print(docs.Path())home/docs/notes free home docs' parent: gone docs free docs free notes free elsewhere
When home takes a new Folder, the old one’s only strong reference is gone, so it’s freed, and
docs.Parent reads as Null. A weak reference’s object can go at any time, so a check doesn’t
narrow it: Path reads Parent into a Local and checks that.
[Weak Self] in a lambda
A lambda that uses its object’s fields or Methods captures Self, and holds a reference to it.
Stored in a field of that object, or of an object it owns, the lambda and the object keep each
other alive. A capture list, Function[Weak Self](), captures Self weakly instead, and inside
the lambda Self?. reaches the object while it exists.
Import std.core
Class Button
Field OnClick: (Func())?
Method Click()
Local handler = OnClick
If handler != Null Then handler()
End Method
Method Destroy()
Print("free the button")
End Method
End Class
Class Counter
Field clicks: Int
Field button: Button
New()
button = Button()
button.OnClick = Function[Weak Self]()
Self?.Bump()
End Function
End New
Method Bump()
clicks++
Print("clicks: {clicks}")
End Method
Method Press()
button.Click()
End Method
Method Destroy()
Print("free the counter")
End Method
End Class
Local counter = Counter()
counter.Press()
counter.Press()clicks: 1 clicks: 2 free the counter free the button
Without [Weak Self], both objects and the lambda would leak. fb warns about the plainest case,
a lambda that captures Self stored in a field of Self, and suggests [Weak Self].
What you learned
- Objects that refer to each other in a cycle are never freed: a leak.
- A
Weak<T>refers to an object without keeping it alive, and reads as aT?. - A tree holds its children strongly and its parent weakly.
- A weak reference isn’t narrowed by a check; read it into a Local first.
Function[Weak Self]()keeps a stored lambda from keeping its object alive.
The rules are in the language reference.
Report an issue with this page or with FutureBlitz on GitHub.