ARC (for Automatic Reference Counting)
ARC is the system which manages automatically the memory allocation / deallocation for your objects.
The way it works is that as long as you'll have AT LEAST 1 strong reference to an object in your code it will remain in memory. Please note that by default any property definition not define as weak is implicitly strong.
When specifying a property as weak it won't increment the "holding reference count" by one.
The issue you are experiencing...
In your first example, when you first create a let constant you hold a strong reference to it, then you assign it to the weak variable.
You have then =>
- 1 weak reference to the object (stored on the
friendsFunctionsDelegate.delegate)
- 1 strong reference to the object (the let constant
let friendsFunctions = FriendsFunctions())
As a result, ARC is NOT deallocating the object (strong reference >= 1) => It is working ✅
In your second example, when you directly instantiate + assign the delegate to the weak variable WITHOUT creating a constant first.
You have then =>
- 1 weak reference to the object (stored on the
friendsFunctionsDelegate.delegate)
- 0 strong reference to the object
As a result, ARC is deallocating (release from memory) the object (strong reference == 0) directly after the assignment => Not working ❌
Conclusion
As a conclusion, you need to keep a strong reference somewhere to that delegate object.
Weak usage in delegate pattern
When using delegation we use a weak property to prevent memory retain cycle. This is happening when you have one object (Object A) holding a strong reference to another object (Object B) which is also having a strong reference to the first object (Object A).
- A => B STRONG
- B => A STRONG
=> ⚠️RETAIN CYCLE⚠️
When you'll try to get rid of object A or B from memory strong reference count will still be 1, you'll then get a memory leak with memory filled with unused objects, which might lead to an usable app. The solution is to define one of those 2 references as weak (not both). When applying the delegate pattern, you would define the property holding the reference to the delegate as weak.
Few extra comments:
- Be careful to the naming which might be misleading. The delegate keyword should be appended only to the protocol and not to the class name. If you remove it though you would have an overlap, which is a hint that the naming could have more appropriately defined.
- When you define a property as Optional and you want it to be nil as default, you don't have to explicitly specify the
= nil. Though you can still do it ;)
- Best practices tell you that when you want a class to conform to a delegate / protocol, you should go with an extension instead of directly conforming at the class definition.
- Delegate pattern should be a blind communication pattern, so your class should not be aware about out of scope functions. Then the naming of the delegate methods should be modified to something like
func didBuyPizza() and func didTakeABreak()or similar.