“This is the 25th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

A weak reference

In Swift we indicate a weak reference by the keyword weak; The weak keyword does not keep a reference to the instance when it is used. Reference type data decorated with the weak keyword does not increment the reference count when passed, does not keep a strong reference to the instance it references, and therefore does not prevent ARC from releasing the referenced instance.

Since a weak reference does not hold a reference to an instance, it is possible for a weak reference to still refer to the instance when it is released. Therefore, ARC automatically sets weak references to nil when the referenced instance is freed. Since a weak reference needs to be allowed to be set to nil, it must be an optional type;

We modify the above code to add the weak keyword to the ClassA attribute in ClassB:

As you can see, memory is freed correctly;

So what does weak tangle do to our code?

We define the following code:

weak var obj3 = ClassA(a)Copy the code

Set a breakpoint, when running this code, we look at the assembly code will find that swift_weakInit is called:

We find swift_weakInit implementation from Swift source code:

It can be seen from the source code that declaring a weak keyword is essentially defining a WeakReference object; So what does the nativeInit method do?

FormWeakReference () is called in the nativeInit method, which means that a weak reference is formed (forming a hash table) :

It can be seen that formWeakReference actually creates a hash table, which is created as follows:

  • Remove the originalA 64 - bitInformation, that isrefCounts
    • If a reference count exists, it is returned;
    • Returns NULL if no reference count exists or is being destructed;
  • The hash table is then createdHeapObjectSideTableEntry;

According to the following source code:

It can be analyzed that there are essentially two kinds of reference counting in Swift:

  • InlineRefCountsStore insidestrong RC.unowned RC.Flags flags; If a reference count currently exists, it will be storedHeapObjectSideTableEntry;
    • If it isStrong reference, it isstrong RC + unowned RC + flags;
    • If it isA weak reference, it isHeapObjectSideTableEntry;
  • HeapObjectSideTableEntryWhat’s stored inside isA 64 - bitThe originalstrong RC + unowned RC + flags, plus32 -theweak RC;
  • InlineRefCountsandSideTableRefCountsIt’s all publicRefCountBitsT;

We can verify this by looking at the implementation of heapObject TableEntry:

RefCounts is of type SideTableRefCounts:

And they all end up pointing to RefCountBitsT;

SideTableRefCounts not only inherits RefCountBitsT but also inherits its 64-bit bit-domain information, as well as 32-bit weakBits.

Let’s look at it in code:

After the code is modified by weak, its memory data changes, we check the hexadecimal find:

After using weak first, its 62nd and 63rd bits are marked as 1, and we restore them to 0:

When we analyze the reference count, the strong reference is counted by the left shift, so the weak reference should be similar principle, we can find the hash offset code in the source:

static const size_t SideTableUnusedLowBits = 3;
Copy the code

0x20E80810 (0x20E80810, 0x20E80810, 0x20E80810, 0x20E80810)

We analyzed its memory data as follows:

We already know that weak references can only modify optional types, and that when the referenced instance is freed, the property is automatically set to nil; The question is, what if we use an attribute of a non-optional type and a circular reference happens to occur? Swift provides us with another keyword unowned(unreferenced);

No primary reference

The biggest difference between an ownerless reference and a weak reference is that an ownerless reference always assumes that the property is non-nil, and if the instance referenced by the property is destroyed and freed, the program will crash if it is used again. Weak references allow the property to be nil and therefore not crash. Compared with the two, weak references are more compatible, while undirected references are less secure.

We changed weak in the above code to unowned:

So for P1, it is declared as an ownerless reference;

So how do you choose between weak and undirected references?

  • Use strong references if there is no relationship between the two lifecyclesweak, such asdelegate;
  • If one object is destroyed and the other object is also destroyedunowned;

Weak is more compatible and secure than unowned, while unowned has higher performance. This is because weak needs to operate on hash tables, whereas unowned only needs to operate on 64-bit domain information; When using unowned, make sure that the attribute it modifies has a value;