Why iOS Developers overlook the usefulness of unowned
Alex Cruz
Posted on February 14, 2024
Most iOS Developers I’ve talked to about unowned variables disregard its specific usefulness. Even worse, they just default to weak without a justified reason. I think this is because it’s not commonly used in our day-to-day development. But if you spend some time digging into Swift’s own documentation around the topic and doing your own hands-on tinkering, you’ll quickly notice what makes unowned important and realize the distinction between weak and owned.
What makes unowned important is that unlike weak variables, unowned variables can be declared as non-optionals. That means we are responsible for assigning them a value during initialization. Which tells us precisely their usefulness, that we must use unowned when we need to establish a tightly coupled relationship between two objects. For instance, when establishing relationships between a customer and a credit card object, we instinctively know that a customer may or may not own a credit card, but a credit card must always be tied to a customer. If it doesn’t have a customer, well, it’s not a credit card, is it? It’s just a piece of plastic.
Since all weak properties are optional, you can create a credit card without a customer, and this is precisely what you want to avoid. The way to avoid it is by using a non-optional unowned property that enforces the relationship safely.
Here’s an example to demonstrate visually unowned’s usefulness:
Weak:
As you can see, if we use weak, we can initialize a credit card with or without a customer. While this would still work, it’s not explicit and we should aim to be explicit when possible. I assume that is why the Swift creators included unowned in the first place.
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
weak var customer: Customer?
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
Unowned:
Here, on the other hand, since we’re using a non-optional unowned, we are required to include the credit card in the initialization. Now by simply looking at this class init method, it’s clear that a credit card must always have a customer, since it’s not possible to create a credit card without one. That’s a level of clarity we can’t achieve if we use a weak variable.
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned var customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
Here’s a framework I use that helps me remember the functionality of each keyword, and helps me identify in which situations they’re most applicable.
Summary
Today we explored the specific difference between unowned and weak. We highlighted the usefulness of using unowned as “clarity” in establishing a reference back to the parent object while avoiding a strong reference cycle, which is the primary purpose of both weak and unowned.
Posted on February 14, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024