Is it true that ARC keeps a count of unowned references to an object?
So, if the strong reference count of an object reaches 0 and the unowned reference count of that object is > 0 the object is de-initialized but not de-allocated? And only when the strong and unowned reference count reaches 0 does it get de-allocated?
I read that in an article, on Medium I think) but I'm not sure it's correct.
First of all, let's be aware that the answers to these questions are all implementation details that we should generally avoid relying on. Now, on to the answers:
Yes, it is true. Each object has three reference counts: the strong count, the unowned count, and the weak count.
The strong count is always stored (but is stored with an adjustment of -1, so a stored 0 means a strong reference count of 1, and a stored 1 means a strong reference count of 2, and so on).
The unowned count is also always stored, with an adjustment of +1 that represents all strong references and is removed at the end of deinitialization.
The weak reference count is only stored after the first weak reference to the object is created. The weak reference count, if it is stored, is stored with a +1 adjustment, which represents all unowned references and is removed after the object is deallocated.
Correct. The object is deinitialized: the
deinit
s of the object's class and all superclasses are run, and any of the object's properties that are themselves references are set to nil. However, the object's memory is not deallocated, because the object's header has to remain valid until the lastunowned
reference to the object is destroyed.Correct. The object is deallocated when both the strong and unowned reference counts reach zero. Since most objects are never referenced by
unowned
references, this is usually when the last strong reference is destroyed.You didn't ask about weak references, but for the sake of completeness, I'll explain them also. When an object is (or has ever been) referenced weakly, Swift allocates what it calls a “side table entry” (or sometimes just “side table”) for the object.
If an object has no side table, then the strong and unowned counts are stored directly in the object, and the weak count (which must be zero) is not stored.
If an object has a side table, then a pointer to the side table is stored in the object. The strong, unowned, and weak counts, and a pointer back to the object, are stored in the side table.
A weak reference to an object is stored as a pointer to the side table, not to the object. This means that an object can be deallocated (not just deinitialized) even if there are still weak references to it.
The side table is deallocated when the object is deallocated if there are no weak references to the object. If there are still weak references, the object is deallocated but the side table remains allocated. When the last weak reference to a deallocated object is destroyed, the side table is deallocated.
Note that weak references are not set to nil (destroyed) immediately when a Swift object is deinitialized or deallocated! A weak reference to a deinitialized object is only set to nil when the program tries to load the reference, or when the container of the weak reference is deinitialized. (What I mean by “the container” is, for example, when an object has a
weak var
property. The object is the container of theweak var
reference.)A large comment at the top of
RefCount.h
in the Swift source code explains all of these details and more.P.S. There is one more kind of reference,
unowned(unsafe)
, which does not adjust any reference counts. You should avoid this kind of reference if at all possible (and avoidance is almost always possible).