Closures and value-capturing
weak/self/unowned
@autoclosure
Metatypes
DispatchQueue.main.async {
print("Async in main thread")
}
[1, 2, 3].forEach { number in
print(number)
}
When we use a closure, most of the time, we need to use variables from the surrounding context
We are able to use these variables by capturing them inside the closure.
getUser(withId: id) { [userRepository] result in
userRepository.addUsers(result.users)
}
Capture list
Closure arg
getUser(withId id: String,
completion: @escaping RequestResult<User>)
{
...
completion(.success(users))
}
struct Calculator {
var a: Int
var b: Int
var sum: Int {
return a + b
}
}
let calculator = Calculator(a: 3, b: 5)
let closure = {
print("The result is \(calculator.sum)")
}
closure() // "The result is 8"
calculator.a = 100
closure() // "The result is 105"
Closure without capturing calculator
var calculator = Calculator(a: 3, b: 5) // 0x618000220400
let closure = {
calculator = Calculator(a: 33, b: 55) // 0x610000221be0
}
print(calculator) // calculator has address 0x618000220400
closure()
print(calculator) // calculator has address 0x610000221be0
Closure without capturing calculator
Calculator "leaks" outside the scope of the closure...
Calculator
closure
We need to "capture" calculator, so that the closure has an immutable copy only... and changes outside the closure's scope won't affect the "local" copy...
let calculator = Calculator(a: 3, b: 5)
let closure = { [calculator]
print("The result is \(calculator.sum)")
}
closure() // "The result is 8"
calculator.a = 100
closure() // "The result is 8"
You can capture multiple reference or value types in
closures...
let closure = { [user, userRepository] item in
userRepository.add(user, item: item)
}
... or even create aliases for them...
let closure = { [face = user, repo = userRepository] in
repo.add(face)
}
Implicit use of 'self' in closure; use 'self.' to
make capture semantics explicit
Relax. The compiler just wants to save you from trouble...
... cause now, your might have a retain cycle!
let closure = { [weak self] item in
self?.addItemToInventory(item)
}
let closure = { [unowned self] item in
self.addItemToInventory(item)
}
@autoclosure attribute enables you to define an argument that automatically gets wrapped in a closure.
It’s primarily used to defer execution of a (potentially expensive) expression to when it’s actually needed, rather than doing it directly when the argument is passed.
But in day to day coding, let's say it just makes some stuff a little more readable...
func animate(_ animation: @escaping () -> Void) {
UIView.animate(withDuration: duration, animations: { animation() })
}
func animate(_ animation: @autoclosure @escaping () -> Void)
UIView.animate(withDuration: duration, animations: animation)
}
String.Self vs String.Type
func printType<T>(of type: T.Type) {
// or you could do "\(T.self)" directly and
// replace `type` parameter with an underscore
print("\(type)")
}
printType(of: Int.self) // this should print Swift.Int
class Superclass {
func f() -> Self { return self }
}
let x = Superclass()
print(type(of: x.f())) // Prints "Superclass"