iosswiftautomatic-ref-countingweak-referencesunowned-references

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


(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.


Solution

  • 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.