Delegates & callbacks in Swift (part 2)
Introduction
In the first part of this tutorial about delegation and callbacks, I had presented the delegation design pattern which is a communication design pattern between two objects based on one-to-one relation ship. We spoke about memory management and how Delegation pattern generates retain cycle and how we can resolve the issue using weak attribut. In this second part, we will find out callback using closure and how we can communicate between two objects using Closure. Indeed, we observe how Callback affect memory management.
Closure in Swift
Closure in Swift like Blocks in Objective C declare and capture a piece of executable code that will be launched at a later time. You can give it a dedicated type name to be referenced and used later like this:
// you can declare type of closure using typealias attribut
typealias completion = (Int) -> Void?
// you can decalare simply like a variable
Closure is simply a function with no name; you can pass it to a variable and pass it around like any other value. The idea is to make the same thing that we made with delegation using closure callbacks. We have usually our two classes ViewController and MyIntOperations. But now, we d’ont need protocol or delegate object to print the value calculated in the ViewController coming from the MyIntOperations instance.
How Closure work ?
Closure is simply a function so we have to call it and a place to define it.
In our case the closure will return the value calculated by the the MyIntOperations instance.
// we add a new parameter to the sum function wihich
// is the closure named callBack and his type is completion
// defined above
func sum(firstNumber:Int, secondNumber:Int,callBack:completion){
// we call our closure function with
// the value calculated
callBack(firstNumber + secondNumber)
}
Now we move to the definition of the closure. What we have to do with this closure. This part of the code exist in the ViewController class
// we call the sum function and also we define
// what we will do with our closure when if will be called
// that it !
myIntOperation.sum(firstNumber: number1, secondNumber: number2) { value in
// define what we will do with the value
// for example simply print it
self.showResult(result: value)
}
private func showResult(result : Int){
print( result)
}
Memory management with Closure
Classes aren’t the only kind of reference type in Swift. functions (which include closure) are references types too. If a closure capture a variable holding a reference type like self.showResult(result:value), the closure will maintain reference to it. We have now a retain cycle because the ViewController keep a reference to the MyIntOperation instance and this one maintain a reference to the ViewController in order to execute the closure callback. But How we can resolve this??? In the definition of the closure we have extra parameters that we name capture list. Basically, we use to define how objects inside the definition of the closure should be referenced. So we have to mention our ViewController (self) as weak
// define self as weak in order to not increase
// reference counting to it by the closure
myIntOperation.sum(firstNumber: number1, secondNumber: number2) {
[weak self] value in
self?.showResult(result: value)
}
Conclusion
Closure is a good solution to handle properly communication between two objects beside it can be a source of retain cycle when closure maintain reference to some objects inside his definition. Closure is based on one-to-one relation ship like delegation. Delegation respect DI inversion principle but closure didn’t satisfy.