The usual explanation is that you should choose between unowned and weak, given the life cycle of the object, but sometimes you might still wonder which should actually be used, and wonder which is better, unowned or weak.
Swift is known to take advantage of good old ARC (automatic reference counting) to manage memory, so just as we are used to objective-C, we have to handle reserved cycle references by judiciously using weak manual handling.
If you’re not familiar with ARC, all you need to know is that each reference type instance will have a reference count (simple integer value) associated with it, which will be used to keep the current count of the object instance referenced by a variable or constant. Once this counter reaches zero, the instances are freed and the memory and resources they hold are available again.
Whenever two instances refer to each other in some way, you have a retention period (for example, two class instances that have attributes that refer to another class instance, like two adjacent node instances in a double-linked list) that prevents those instances from being released, because the retention count is always greater than zero.
To solve this problem, in Swift, as in many other languages, the concept of weak references is introduced, which ARC does not consider and therefore does not increase the retention count of objects.
Given that weak references do not prevent instances from being released, you must always remember that a weak reference can no longer point to a valid object.
Swift has two types of weak references: unowned and weak.
Although they serve the same purpose, they have slightly different assumptions related to the life cycle of your instance and have different performance characteristics.
In Objective-C, following the standard pattern, you declare a weak reference to the instance outside the block, and then declare a strong reference to the instance inside the block so that it can be retrieved during block execution. Obviously, it is necessary to check that the reference is still valid.
To help deal with retention cycles, Swift has introduced a new construct to simplify and more explicitly capture external variables within closures, namely capture lists. With a capture list, you can declare at the top of the function the external variables that will be used to specify what kind of reference should be created internally.
1. Closures create strong references to values in an external scope when you are not using a capture list:
Changes made within closures change the value of the original variable
2. Using the capture list, create a new constant that is valid within the closure scope.
If no capture modifier is specified, the constant is just a copy of the original value, and applies to both value types and reference types.
In the above example, iCopy declares the function iStrong before calling it, and when the declared function initializes the private constant. As you can see, when we call the second function, we still print the original value i1.
3. Before specifying the name of an external variable with a reference type, weak or unowned, this constant will be initialized as a weak reference to the original value, and this particular form of capture is the form we use to interrupt the retention cycle.
It is never nil with unowned and optional with weak
So if the life cycles of these objects are unrelated, that is, you can’t guarantee that one object will live longer than the other, then weak references are the only option. On the other hand, unowned references are usually more convenient if you can guarantee that non-strongly referenced objects have the same or longer lifetime as strongly referenced objects. This is because we don’t need to deal with optional values, and variables will be declared by let, in contrast to weak references that must be declared as optional var. The same life cycle is common, especially when two objects have a master-slave relationship. A backreference by a child to a master object can be a unowned reference when the master object controls the life cycle of the child object through strong references, and you can be sure that no other object knows about the existence of the child.
An unowned reference has less performance cost than a weak reference, so it is slightly faster to access a property of a unowned reference or call a method on it; However, this factor should only be considered on code paths where performance is important.
The downside of unowned references, of course, is that if you make the wrong assumptions about the lifecycle, your application will crash. Personally, I usually choose weak when unowned is also available. Weak forces us to check that references are still valid wherever they are used. We may refactor some code from time to time, and this may invalidate our assumptions about the object lifecycle. See which aspect you value more