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

RevenueCat makes adding subscriptions to your app simple🚀

Never worry about StoreKit 🤦‍♂️📱

Plus, get out-of-the-box charts and reporting for your app 📈📊

👉 Try it for free 👈


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, an unowned reference doesn’t keep a strong hold on the instance it refers to.

Unlike a weak reference, an unowned reference is expected to always have a value. As a result, marking a value as unowned doesn’t make it optional, and ARC never sets an unowned reference’s value to nil.

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!

❤️

Previous
Previous

Discover localizedStandardCompare()

Next
Next

Bad practice: using if instead of guard