Closures are self-contained blocks of functinality that can be PASSED around and USED in your code
-> Closure is a 1st class Object
Closures can CAPTURE and STORE REFERENCES to any constants and variables from the CONTEXT which they ayre defined
-> by reference mean not value, means, If we change any thing inside the closure, the object outsite the closure will also be affected
Closures are Reference Types
-> If I assign a cloure to two var, they actually refer to the same closure
escape: mean the close will be STORED somewhere to execute later (and of course, its context also being STORED and this is the main reason cause retain cycles). Usually meet as api call
DispatchQueue.main.async {
...
}
non-escape: the close will be execute and return immediately, not being store, this is the trivia case. Usually meet when use a function in a collection:
[1,2,3].forEach { number in
...
}
If a closure is a property, It's auto escaping
// declare a callback being call a user is picked
var onUserPick: ((_ user: User) -> Void)?
- Values are capture when being ACCESS (RUN)
var value = 1
let checkValue = { () -> Void in
print("value in closure: \(value)") // 2, not 1, because we change value before call
value = 3 // change to three
}
value = 2 // change value affect in closure because I don't pass it in captures list
print("value before closure: \(value)") // 2: trivia, value has been changed
checkValue()
print("value after closure: \(value)") // 3: value is updated inside the closure
So the whole life cycle of value: 1 -> 2 -> 3
- If using
captures list
the values are capture/copy when being PASSED (DEFINITION)
var value = 1
let checkValue = { [copyOfValue = value] () -> Void in
print("value in closure: \(copyOfValue)") // 1: because it has been copy at passing time
// copyOfValue = 3 // cannot change value of parameter
}
value = 2 // change value is NOT affect in closure, because I used captures list
print("value before closure: \(value)") // 2: trivia, value has been changed
checkValue()
print("value after closure: \(value)") // 2: pass by value, not effect in closure
So the whole life cycle of value: 1 -> 1(copied) -> 2 -> 2
- If the value is an Object, value is always pass by references whethe using
captures list
or not because object always a pointer
var user = User(name: "Kenji")
let checkValue = { [copyOfUser = user] () -> Void in
print("value in closure \(copyOfUser.name)"); // Shinja
copyOfUser.name = "Otomo" // can change the props of object
}
user.name = "Shinja"
print("value before call closure \(user.name)") // Shinja
checkValue();
print("value after after call closure \(user.name)") // Otomo
Just a convenient way to write closure when it is the last argument in a function. When call that function, you can open parenthesis at the end (that why it is called trailing)
// define
func foo(closure: () -> Void) {
... foo body
}
// calling
foo(closure: {
... closure body
});