How risky is it to use [unowned self]? 🤔
Hi 👋
Before I start this email, I have a big thank you to my returning sponsor for the next two weeks: RevenueCat 😼
Advertisement
Sponsors like RevenueCat really help me grow my content creation, so if you have time please make sure to have a look at what they offer: it’s a direct support to my content creation ☺️
I’m sure you already know about retain cycles and how they can be broken by capturing a weak
reference to an instance.
(and if you’re not sure what a retain cycle is, here’s a 1-minute recap)
You might have also heard that, instead of a weak
reference, you can also capture an unowned
reference.
And you might have even heard that unowned
reference are a bit more optimized, but also a bit more risky.
So what’s the difference between weak
and unowned
references?
To answer this question, a good place to start is the official documentation, which states:
Like a
weak
reference, anunowned
reference doesn’t keep a strong hold on the instance it refers to.Unlike a
weak
reference, anunowned
reference is expected to always have a value. As a result, marking a value asunowned
doesn’t make it optional, and ARC never sets anunowned
reference’s value tonil
.
From that quote, we can understand why an unowned
reference would be more optimized.
Because the compiler assumes the instance will still be in memory by the time the reference is used, it won’t be wrapping the reference inside an Optional
nor will it deal with the case where the instance is deallocated while the reference is still in memory.
This means that using an unowned
reference leads to less overhead than using a weak
reference, thus resulting in a more optimized code.
However, if we keep reading the documentation, we also learn that this optimization comes at a cost:
Use an
unowned
reference only when you are sure that the reference always refers to an instance that hasn’t 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.
This means that unowned
references behave quite similarly to force unwraps!
If the instance is still in memory when the reference is used, all goes well; but if the instance has been deallocated, then you get a crash 💥
However, if we are sure that the instance will always be in memory when the reference is used, then according to the documentation we should definitely prefer using an unowned
reference over a weak
reference.
But, as you can imagine, there’s a catch!
And that catch is that it’s actually very easy to mistakenly think that it’s safe to use an unowned
reference.
Let me show you an example:
In this code, our ViewModel
makes an asynchronous call to its service
, and uses an unowned
reference to self
in the completionHandler
of that call.
At first glance, this use of an unowned
reference seems reasonable: because service
is a private property of the ViewModel
, it will indeed be deallocated at the same time than the ViewModel
.
However, it’s important to notice that this code doesn’t show us how the service
uses the closure that captured the unowned
reference.
If we now take a look at how the Service
is implemented, then the picture changes completely:
As you can see, the Service
calls the completionHandler
when the dataTask
completes.
But the thing is, there’s absolutely no guarantee that the ViewModel
(and the Service
) will still be in memory at that time.
So it’s totally possible that when the dataTask
completes, the ViewModel
will have already been deallocated, resulting into a crash of the app 💥
So what should you take away from this example?
The example showed us how easy it is to get the false sense of security that it’s safe to use an unowned reference.
So my personal advice on this topic is that, unless you are working on low-level code that can really benefit from the performance gain of using unowned
, it’s safer to use always use weak
references.
To be honest, I don’t think I have ever been the situation where I needed the gain in performance, however I definitely have seen quite a number of crashes that were caused by a faulty unowned
reference…
(and many of these crashes were quite difficult to reproduce and fix!)
That’s all for this email, thanks for reading it!
If you’ve enjoyed it, feel free to forward it
to your friends and colleagues 🙌
I wish you an amazing week!
❤️