Using self, weak, and unowned in Combine
Updated with more examples on Mar 6th, 2021.
One of the most often asked questions in the Combine and RxSwift slack channels is something along the lines of:
Should I use self, weak, or unowned with my reactive code?
Given that most operators take closures, it’s a fair question. In this post I’ll go over common scenarios for using weak
, unowned
or simply self
and include links with more information at the bottom.
Important: Combine code is still plain Swift - memory management works exactly as in any other of your Swift apps. This brief post only aims to give you few examples to refer to if you are unsure of the details.
Use [weak] when an object might get released
Tip: Use
weak
only if you’re capturing a class instance.
Use weak
when you use a class instance in an escaping closure in one of your operators. When you call a method on your view controller, change a property on a view, or access another class instance, you likely need to capture it weakly.
|
|
The sink
closure above is called asynchronously. It might happen that when the closure is (finally) executed MyViewController
is already dismissed by the user, and released from memory.
In such case self
is nil
- you need to safely access it as self?
or alternatively unwrap it with a guard
:
|
|
Tip: Capturing objects weakly prevents memory retain cycles.
Use [unowned] for objects guaranteed to exist
Tip: Use
unowned
only if you’re capturing a class instance.
Use unowned
in an escaping closure to access an object that is guaranteed to exist and avoid memory retain cycles by not retaining that object from the closure.
Such objects are your app delegate (which usually lives as long as your app is running), the app’s root view controller, a shared cache object, or any other object you know for certain will be present in memory for as long as your closure might get called.
|
|
Warning: If
self
is released from memory and your subscription is invoked - accessingself
will crash your app.
Use self to explicitly capture an object or a value
If you’re capturing a struct or another value type like an Int
or a String
you don’t need to be concerned about memory management. Reference self
or another value and Swift will figure the memory management automatically:
|
|
Implicitly capture strongly an object by not specifying weak
or unowned
. This will ensure the object is retained in memory for as long as your closure might get called.
Here’s an example how to weakly capture self
but also strongly another object which you need and no-one else might be retaining:
|
|
assign(to:on:) captures strongly
Use assign(to:on:)
to capture the subscriber of a subcription, as long as you’re not creating a cyclic reference.
The subscription below captures self
because you use it with assign(to:on:)
and MyViewController
leaks (aka is never released from memory).
|
|
Instead, use assign(to:on:)
safely when you don’t capture self
; for example to bind updates from the network to a data model:
|
|
What happens if I capture self strongly?
Try using self
directly like so inside a sink(...)
:
|
|
You retain the escaping closure used in sink
via retaining the subscription in the class’ sub
property. At the same time you retain self
back from within the same escaping closure. Each of the two retains the other one in a cyclic reference.
See the effect of this cyclic retain by running this code:
|
|
Creating a new Model
initializes the timer subscription and the app starts printing dates every second. Unfortunately setting model
to nil
doesn’t release it from memory as the closure used in sink
keeps it alive.
Note: After you do
model = nil
there is no way to access the model that is just hanging around in memory anymore. That’s why we call this a “memory leak”.
Now let’s try not capturing self
by using [weak]
- all of the code stays the same except the code inside sink
:
|
|
Now you retain the escaping closure via retaining the subscription from Model
, but you don’t retain the model back from the closure. As soon as you set the local variable model
to nil
that releases the subscription and the closure too.
|
|
This time around the code doesn’t print anything anymore as the model and the timer subscription are released immediately after they are created.
Where to go from here?
If you haven’t read it, the Swift book’s “Automatic Reference Counting” chapter is a great read and covers in detail the concepts we touched upon in here and more.
Hit me up with your ideas on Twitter at https://twitter.com/icanzilb.