Why weakifying a strong reference by using a local variable doesn't work?

529 views Asked by At

(I understand how ARC works and the difference between weak and unowned. The question is about a specific use of them and why it doesn't work. I'll use unowned in the example below just for simplicity.)

See example below. Note line 10, which is intended to change the passed strong reference to an unowned reference. I thought this would work, but when I used it in my code recently I found I was wrong.

 1  import Foundation
   
 2  class MyClass {
 3      var uuid: UUID = UUID()
 4      
 5      deinit {
 6          print("Deinited!")
 7      }
 8  }
   
 9  func test(_ d: inout [UUID:MyClass], _ o: MyClass) {
10      unowned let u = o  // <- !
11      d[u.uuid] = u
12  }
   
13  var d = [UUID: MyClass]()
14  test(&d, MyClass())

Run the above code in Playground. The result shows that deinit isn't called, indicating a strong reference to the object is saved in the dictionary.

I wonder why? Does weak and unowned keyword only applies to property? But the above code doesn't generate compiler error and the Swift book does mention it can be used in variable declaration:

You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.

Can anyone share how you understand it? Thanks!

BTW, I know how to solve the issue (for example, using a wrapper like this). What I try to understand is why the above code doesn't work.

2

There are 2 answers

1
Sweeper On BEST ANSWER

When assigning (a = b), you can't control what kind of reference a is. You can only control what object a refer to.

Here:

d[u.uuid] = u

The code is not saying:

The value associated with the key u.uuid in d is set to the (unowned) reference u.

It's saying:

The value associated with the key u.uuid in d is a reference (not saying what kind) that refers to what u is referring to.

The fact that u is unowned is rather irrelevant. Dictionaries will always store strong references to objects. That's just how they are designed.

In the case of the Weak<T> wrapper, the dictionary will still store a strong reference to Weak<T>, but Weak<T> will store a weak reference to the wrapped T object. That's how it achieves not having a strong reference to the T object.

0
rayx On

@Sweeper's answer contains all the points I'll give below. But I'll try to make them a bit more explicit:

  • Variable represents a storage (a location in memory)
  • Assignment operation changes the value in the storage
  • weak and unowned are attributes of the storage. They are not affected by assignment operation.

So the unowned keyword in line 10 in my example code affects only that specific storage location (it's in stack) and have no influence on the attribute of storage used by the global dictionary (as @Sweeper pointed out).