weak vs unowned in Swift. What are the internal differences?
Asked Answered
S

2

22

I understand the usage and superficial differences between weak and unowned in Swift:

The simplest examples I've seen is that if there is a Dog and a Bone, the Bone may have a weak reference to the Dog (and vice versa) because the each can exist independent of each other.

On the other hand, in the case of a Human and a Heart, the Heart may have an unowned reference to the human, because as soon as the Human becomes... "dereferenced", the Heart can no longer reasonably be accessed. That and the classic example with the Customer and the CreditCard.

So this is not a duplicate of questions asking about that.


My question is, what is the point in having two such similar concepts? What are the internal differences that necessitate having two keywords for what seem essentially 99% the same thing? The question is WHY the differences exist, not what the differences are.

Given that we can just set up a variable like this: weak var customer: Customer!, the advantage of unowned variables being non-optional is a moot point.

The only practical advantage I can see of using unowned vs implicitly unwrapping a weak variable via ! is that we can make unowned references constant via let.

... and that maybe the compiler can make more effective optimizations for that reason.

Is that true, or is there something else happening behind the scenes that provides a compelling argument to keeping both keywords (even though the slight distinction is – based on Stack Overflow traffic – evidently confusing to new and experienced developers alike).

I'd be most interested to hear from people who have worked on the Swift compiler (or other compilers).

Saar answered 16/3, 2017 at 18:40 Comment(4)
Possible duplicate of What is the difference between a weak reference and an unowned reference?Capita
Related: Swift. Is the (absolutely) sole specific advantage of unowned over weak, performance?Engraving
@DalijaPrasnikar Disagree. The OP is asking how the implementations differ; the question you linked was about conceptual (i.e., usage) differences.Proclivity
NOTE: There are important performance implications to be aware of with each of these references: #58635803Yuu
C
21

My question is, what is the point in having two such similar concepts? What are the internal differences that necessitate having two keywords for what seem essentially 99% the same thing?

They are not at all similar. They are as different as they can be.

  • weak is a highly complex concept, introduced when ARC was introduced. It performs the near-miraculous task of allowing you to prevent a retain a cycle (by avoiding a strong reference) without risking a crash from a dangling pointer when the referenced object goes out of existence — something that used to happen all the time before ARC was introduced.

  • unowned, on the other hand, is non-ARC weak (to be specific, it is the same as non-ARC assign). It is what we used to have to risk, it is what caused so many crashes, before ARC was introduced. It is highly dangerous, because you can get a dangling pointer and a crash if the referenced object goes out of existence.

The reason for the difference is that weak, in order to perform its miracle, involves a lot of extra overhead for the runtime, inserted behind the scenes by the compiler. weak references are memory-managed for you. In particular, the runtime must maintain a scratchpad of all references marked in this way, keeping track of them so that if an object weakly referenced goes out of existence, the runtime can locate that reference and replace it by nil to prevent a dangling pointer.

In Swift, as a consequence, a weak reference is always to an Optional (exactly so that it can be replaced by nil). This is an additional source of overhead, because working with an Optional entails extra work, as it must always be unwrapped in order to get anything done with it.

For this reason, unowned is always to be preferred wherever it is applicable. But never use it unless it is absolutely safe to do so! With unowned, you are throwing away automatic memory management and safety. You are deliberately reverting to the bad old days before ARC.

In my usage, the common case arises in situations where a closure needs a capture list involving self in order to avoid a retain cycle. In such a situation, it is almost always possible to say [unowned self] in the capture list. When we do:

  • It is more convenient for the programmer because there is nothing to unwrap. [weak self] would be an Optional in need of unwrapping in order to use it.

  • It is more efficient, partly for the same reason (unwrapping always adds an extra level of indirection) and partly because it is one fewer weak reference for the runtime's scratchpad list to keep track of.

Chyou answered 17/3, 2017 at 1:45 Comment(6)
Thanks, this is the best answer so far, actually going into what happens behind the scenes on the compiler level. Do you have a reference for the idea that weak is computationally intensive?Saar
You just have to imagine what it takes for ARC to do all that bookkeeping behind the scenes. The process is well described in the wonderful WWDC 2012 video 406 where ARC was introduced. Everyone should watch this video, because Swift ARC is Objective-C ARC (in fact, we now know that Chris Lattner invented ARC for Objective-C exactly so that he could create Swift).Chyou
In any case, the point of my answer is really that your question is based on a false premise. The difference between weak and unowned may seem just like a substitution of one word for another to you, but they are completely different things — indeed, they are different kinds of thing. You might want to read my online book, where I discuss memory management policies for synthesized properties (this is essentially the same as what you're asking about) apeth.com/iOSBook/…weak is weak, assign is unowned.Chyou
They seem like a substitution of one word for another for anyone who hasn't asked the question. So the "premise" of the question that you're skeptical of is the question...Saar
@Saar Okay, I see what you mean! My problem was the question started "I understand" and I'm thinking, "No, you don't". But now I see that you're saying: I know the rule that says "use unowned only if the referenced object always exists and is essential to the life of this object". What you wanted to know is why that is the rule (and secondarily if there is ever a case when you positively should use unowned rather than weak, since clearly you always can use weak). And you're right, that's a perfectly good thing to ask. (I hope I've explained it satisfactorily.)Chyou
"involves a lot of extra overhead for the runtime", so you're saying 1 nanosecond is an a lot of overhead? Do you have some measurements to say that? On my experience you can use weak just everywhere, and it won't penalty your performance, especially in some UI apps.Bidet
J
9

A weak reference is actually set to nil and you must check it when the referent deallocates and an unowned one is set to nil, but you are not forced to check it.

You can check a weak against nil with if let, guard, ?, etc, but it makes no sense to check an unowned, because you think that is impossible. If you are wrong, you crash.

I have found that in-practice, I never use unowned. There is a minuscule performance penalty, but the extra safety from using weak is worth it to me.

I would leave unowned usage to very specific code that needs to be optimized, not general app code.

The "why does it exist" that you are looking for is that Swift is meant to be able to write system code (like OS kernels) and if they didn't have the most basic primitives with no extra behavior, they could not do that.

NOTE: I had previously said in this answer that unowned is not set to nil. That is wrong, a bare unowned is set to nil. A unowned(unsafe) is not set to nil and could be a dangling pointer. This is for high-performance needs and should generally not be in application code.

Jackelinejackelyn answered 16/3, 2017 at 18:44 Comment(3)
You seem to have hit the popular consensus with this one (that weak is more common than unowned) but I'm not sure this answers the question. It seems the answer describes the superficial usage differences rather than the internal reasoning behind why the two exist as separate keywords. The comment about Swift also being designed for systems programming is a good one though, thanks.Saar
Another key point revisiting your answer is that you're not guaranteed unowned references will be set to nil (I'm guessing they generally won't be and will be left as dangerous dangling pointers). Thanks.Saar
There are unowned(unsafe) which become dangling pointers. I'll update answer.Jackelinejackelyn

© 2022 - 2024 — McMap. All rights reserved.