Delegates & callbacks in Swift (part 1)
Introduction
Callbacks and delegation are two of the most commonly used tools by Swift developers. In this article, I will try to explain the differences between them from the point of view of usage and memory management.
What is delegation?
Delegation is a common design pattern where one class/struct delegates the responsibility of implementing some functionality to another (the delegated class or struct). In order to make communication between the delegates and the delegated modules, we use a kind of contract called a protocol.
- Protocol:
A protocol specifies the initializers, properties, functions, and subscripts required for a Swift object type (class, struct, enum) to conform to the protocol.
For example, we can create a protocol calledShowResultthat shows a result after making some arithmetics operations.
public protocol ShowResult{func show(value:Int)}
A class, struct, or enum can implement this protocol as follows:
extension ViewController: ShowResult{func show(value: Int) {print(value)}}
Now we understand protocols and how to implement them using extension, but what about delegation?!
- Delegation and protocol:
Now, imagine that we have aUIViewControllerthat we want to use for some mathematics operations, but we don’t want to implement the mathematics functions inside ourViewControllerclass. Maybe we should do this task in a second class or struct .
Basically, we should have an instance of the second class/struct in theViewControllerclass. This is ourMyIntOperationinstance that, when it finishes its work, can delegate the display to ourViewControllerclass :). TheViewControllerobject will observe any event launched by theMyIntOperationobject, so we have a one-to-one relationship.
class MyIntOperation {weak var delegate: ShowResult?func sum(firstNumber:Int, secondNumber:Int){// Make arithmetic operation and delegate display to the ViewControllerdelegate?.show(value: firstNumber + secondNumber)}}
The delegate type is a protocol, and every class/struct that implements this protocol can make the call. Maybe when we make some unit tests for the MyOperationClass, we won’t be dependent on the ViewController instance 😃.
We notice that the delegation pattern respects the fifth principle of SOLID because we only infer to abstractions in the ShowResult protocol, not to concretions.
- Memory management:
Memory management is very important in any application. Swift uses Automatic References Counting (ARC) to keep track of the strong references to instances of classes, and to increase or decrease their reference count. ARC does not increase or decrease the reference count for value types, like struct or enum, because these are copied when assigned.
3.1. Strong reference cycle:
The retain cycle is when two classes depend on each other, so we cannot release any of them. But why?
TheViewControllerkeeps a reference of theMyIntOperation, which keeps a reference of the delegate that is simply aViewControllerinstance. This is why ARC cannot release theViewControllerwhen, for example, we go to a second view.
3.2 Weak reference:
When the ViewController is created, ARC increases the reference count to 1. When MyIntOperation keeps a reference to the ViewController (delegate is simply a reference to it), ARC increments the count to 2. Imagine that we want to push a new ViewController(i.e., when we call the system’s Deinit method because the instance should be nil), but since we have a delegate reference kept by the MyIntOperation object, our retain count will be equal to 1 (not zero). Therefore, we cannot release our ViewController instance.
The solution is to declare the delegate weak so that the retain count will not be increased
and ARC can deallocate the ViewController instance.
Conclusion
In this first part, we studied the delegation design pattern and the relationship it has with memory management. The main idea is that this pattern is a communication type pattern that could be a source of retain cycles. It gives a good opportunity to apply DI principles based on protocols. In the second part of this tutorial, we will speak about closure callback and how we can observe an event between objects using a closure.
Free Resources
- undefined by undefined