Escaping and Autoclosures
Introduction
Welcome to the last lesson of Swift Memory Management. You will learn at which circumstances you use escaping
and autoclosures
. No fear closures.
Problem
What are those?
Escaping Closures
Definition: If the closure block is executed after the function returns, you must add
@escaping
.
Scenario 1
Create an array whose type is [() -> Void]
.
var closureBlocks: [() -> Void] = []
Create function whose parameter type is () -> Void
. The function will take the parameter and append to the closureBlocks
array.
func funcWithClosure(closure: @escaping () -> Void) {
closureBlocks.append(closure)
}
The @escaping
keyword is added since the closure can be called after the function returns/executes.
Scenario 2
Asynchronous operation does not execute code line-by-line. Instead, it runs many at the same time.
import Foundation
func funcWithNetworkingClosure(closure: @escaping () -> Void) {
DispatchQueue.global().async {
closure() // Ex) downloading
}
}
The @escaping
is added automatically.
When you pass a closure block to the function, the closure block is executed even after the function returns.
funcWithNetworkingClosure {
for _ in 1...1000 {
print("downloading")
}
}
// "downloading"
// "downloading"
// ...
// execute other operations
// "downloading"
// "downloading"
// execute other operations
Note: If you wish to learn more about multi-tasking and asynchronous operations,, you may read Intro to Grand Central Dispatch in Swift 3 with Bob
Normal Function
A normal function is @nonescaping
by default. In other words, the closure is executed before the function returns/finishes.
class Normal {
let name = "Bob"
func normalFunctionWithClosure(closure: (String) -> Void) {
closure(self.name)
}
}
Normal().normalFunctionWithClosure { (myName) in
print(myName)
}
When you execute the function, the closure block is initialized and deallocated as the function returns. The function also gets deallocated. It happens almost simultaneously. If the function is @escaping
, however, the closure block is not deallocated while the function block is.
Benefits of Non-Escaping
- There is no retain cycle for non-escaping functions since everything is deallocated
- You may use
self
keyword without worrying about memory leak - Performance and the ability for the compiler to optimize
Introducing @autoclosures
func checkIDCard(hasCard: () -> Bool) {
if hasCard() {
print("You've an ID")
}
}
checkIDCard(hasCard: { return true})
checkIDCard(hasCard: { true })
checkIDCard { true }
The @autoclosure
keyword allows you to pass a closure block with no brackets.
func checkIDCard(hasCard: @autoclosure () -> Bool) {
if hasCard() {
print("You've an ID")
}
}
checkIDCard(hasCard: true)
Lazy Init Capture
You’ve learned how to initialize an object with lazy
and closures. However, you must be careful of potential retain cycle. Let us find out if there is one.
Create Lazy
class BobGreet {
var name = "Bob the Developer"
lazy var greeting: String = {
return "Hello, \(self.name)"
}()
deinit {
print("I'm gone, bruh ?")}
}
The closure block as a strong
reference to self
. However, let us attempt to deallocate regardless.
Deallocate
var bobGreet: BobGreet? = BobGreet()
bobGreet?.greeting
bobGreet = nil // "I'm gone, bruh ?"
Important: a closure block with a
lazy
property is@noescape
by default.
Source Code
5004_escaping_autoclosures.playground
Resources
Intro to Grand Central Dispatch in Swift 3 with Bob
Conclusion
You’ve learned 3 concepts. First, a function requires the @escaping
keyword if the closure block in the parameter is executed after the function returns. Second, the @auotoclosure
keyword is used to enter a closure block without brackets. However, the closure must have no parameters. Lastly, there is no retain cycle in lazy
properties.
Note: Learn Swift with Bob is available on Udemy. If you wish to receive a discount link, you may sign up here.