Skip to content

Instantly share code, notes, and snippets.

@tranquan
Created January 6, 2018 09:14
Show Gist options
  • Save tranquan/b88c3d51af39cbdbba92bbc3a77a6fa2 to your computer and use it in GitHub Desktop.
Save tranquan/b88c3d51af39cbdbba92bbc3a77a6fa2 to your computer and use it in GitHub Desktop.
A Summary and Best practices of Closure in Swift

Documents

What is closure?

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 vs Non-Escaping

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)?

Capture Values

  • 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

Trailing Closure

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
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment