1

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

rayx
  • 1,329
  • 10
  • 23
  • 1
    I'm not sure why you would expect otherwise. Dictionaries are designed to hold strong references to things. That's just how they work. That extra unowned reference to what `o` refers to, called `u`, is completely irrelevant to how the dictionary stores its references. – Sweeper Apr 04 '21 at 03:07
  • @Sweeper This is where I'm confused. So, do you mean that it depends on the type that stores the reference to decide whether strong or weak reference is stored? I thought it depends on the type of the reference the caller passes to the type (dictionary in this case). I believe I'm missing something. Would you elaborate it a bit? – rayx Apr 04 '21 at 03:13
  • Let me put it this way. If I passes a weak reference to a dictionary, how can it store strong reference? The real underlying question is: how exactly is strong/weak reference implemented? (I have searched for information about this but haven't found any yet). – rayx Apr 04 '21 at 03:15
  • 1
    "Exactly how strong/weak references are implemented" isn't particularly related to this question, I feel. It seems like you just have a misunderstanding of what `=` means, which I have clarified in my answer. – Sweeper Apr 04 '21 at 03:22

2 Answers2

2

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.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

@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).

rayx
  • 1,329
  • 10
  • 23