Difference between unowned var something: Something! and weak var something: Something! in Swift

78 views Asked by At

Consider the following example given below:

protocol Something: AnyObject { 
    func f()
}

class A {
    unowned var something1: Something!
    weak var something2: Something!

    func f() {
        something1.f()
        something2.f()
    }
}

What is the difference between type of something1 and something2?

1

There are 1 answers

0
HangarRash On

Quick summary:

weak var something2: Something! is bad since you are effectively force-unwrapping a variable that is assumed (via weak) to be nil at some future time after being set.

unowned var something1: Something! is better. The use of unowned is only to be used when it is assumed that the variable reference will remain valid longer than the lifetime of the variable. Making it implicitly unwrapped assumes it will never become nil once given a value.

Details. Let's break this down into parts.

  1. What's the difference between weak and unowned?

    From the ARC chapter in the Swift book - Weak References:

    A weak reference is a reference that doesn’t keep a strong hold on the instance it refers to, and so doesn’t stop ARC from disposing of the referenced instance. This behavior prevents the reference from becoming part of a strong reference cycle. You indicate a weak reference by placing the weak keyword before a property or variable declaration.

    Because a weak reference doesn’t keep a strong hold on the instance it refers to, it’s possible for that instance to be deallocated while the weak reference is still referring to it. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. And, because weak references need to allow their value to be changed to nil at runtime, they’re always declared as variables, rather than constants, of an optional type.

    From the ARC chapter in the Swift book - Unowned References:

    Like a weak reference, an unowned reference doesn’t keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime. You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.

    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 the ARC chapter in the Swift book - Unowned Optionals:

    You can mark an optional reference to a class as unowned. In terms of the ARC ownership model, an unowned optional reference and a weak reference can both be used in the same contexts. The difference is that when you use an unowned optional reference, you’re responsible for making sure it always refers to a valid object or is set to nil.

  2. What is an implicitly unwrapped optional (IUO)?

    From the Basics chapter in the Swift book - Implicitly Unwrapped Optionals:

    As described above, optionals indicate that a constant or variable is allowed to have “no value”. Optionals can be checked with an if statement to see if a value exists, and can be conditionally unwrapped with optional binding to access the optional’s value if it does exist.

    Sometimes it’s clear from a program’s structure that an optional will always have a value, after that value is first set. In these cases, it’s useful to remove the need to check and unwrap the optional’s value every time it’s accessed, because it can be safely assumed to have a value all of the time.

What does this mean for weak var something2: Something! ?

Based on the description of weak and of IUO, having a variable that is both weak and IUO is a contradiction. weak is assumed that it will be set to nil at some point after being set. IUO is assumed that it will not be nil once given a value.

So if you have weak var something2: Something! and later you try something2.f(), your program will crash if something2 has been set to nil either directly or due to ARC setting it to nil due to the reference being deallocated.

The use of the IUO is your way of telling the compiler "trust me, it will never be nil". But the use of weak is telling the compiler "careful, it might be nil". That's a marriage doomed for a divorce.

What does this mean for unowned var something1: Something! ?

Unlike weak, unowned variables can be either optional or non-optional. An IUO is an optional but it precludes the need to handle it like an optional. So an unowned IUO is an optional.

Based on the description of unowned and of IUO, having a variable that is both unowned and IUO is a bit redundant but useful. Making a variable unowned means you ensure it maintains a non-nil value once set. IUO is assumed that it will not be nil once given a value. So both have the same goal.

Being unowned, it doesn't hold a strong reference so it has that benefit of weak. But being an optional unowned means you are responsible to ensure it maintains a reference to a valid object or is explicitly set to nil (since ARC will not do so for you). Being IUO, you are assuming it will never be nil so you don't have to add needed syntax normally used with optionals.

So if you have unowned var something1: Something! and later you try something1.f(), your program will crash if something1 references a deallocated object or if it has explicitly been set to nil.

Summary:

Don't use a weak var something: Something!. It's a crash waiting to happen.

Use unowned var something: Something if you can set the value in an initializer or declared scope, you don't want a strong reference, and the reference will remain valid once set.

Use unowned var something: Something! if you can't set the value in an initializer or declared scope, you won't attempt to access the variable until set, you don't want a strong reference, and the reference will remain valid once the initial value is set.

Use unowned var something: Something? if you don't want a strong reference, the reference will remain valid while set, and you may want to explicitly set the value to nil at some point.

Keep in mind that all uses of unowned can cause a crash if the referenced instance has been deallocated. So only use unowned when you know that won't happen.