Swift Closures
Closures - Closure Expressions, Trailing Closures, Capturing Values, Escaping Closures, Autoclosures, Lambda
1. Closure Expressions π©βπ»
Swift μ Closures
λ μ μλ context(or scope)
μ μμλ λ³μλ₯Ό μΊ‘μ²νκ³ μ μ₯ν μ μλ€.
μ΄λ λ€λ₯Έ μΈμ΄μ Lambda expressions
μ λΉμ·νλ€.
1 ) Closures λ λ€μ μΈ κ°μ§ νν μ€ νλλ₯Ό κ°λλ€
- Global Functions : μ΄λ¦μ΄ μκ³ , μ΄λ€ κ°λ μΊ‘μ²νμ§ μλ Closures
- Nested Functions : μ΄λ¦μ΄ μκ³ , μμ μ΄ μν
function context
μ κ°μ μΊ‘μ²ν μ μλ Closures - Closure Expressions : μ΄λ¦μ΄ μκ³ , μμ μ΄ μν
context
μ κ°μ μΊ‘μ²ν μ μλ κ²½λνλ Closures
2 ) Swift Closures μ΅μ νκ° κ°λ νΉμ§
context
λ‘λΆν° parameter μ return value λ₯ΌμΆλ‘ (inferring)
νλ€- λ¨μΌ νν ν΄λ‘μ (single-expression closures)μμ μμμ λ°ν
implicit returns
- μΆμ½λ μΈμ μ΄λ¦
shorthand argument names
- νμ ν΄λ‘μ λ¬Έλ²
trailing closure syntax
Closure Expressions
λ μ΄λ¦μ΄ μμΌλ κ²½λνλ λ¬Έλ²μΌλ‘ μ¬μ¬μ© λμ§ μλ ν¨μ, μ£Όλ‘Nested Functions
λ₯Ό λ체ν μ μλ€.
1. The Sorted Method
sorted(by:)
λ©μλλ, Comparable
νλ‘ν μ½μ λ°λ₯΄λ νμ
μ λν΄ (Type, Type) -> Bool
νμ
μ Closures λ₯Ό
arguments λ‘ λ°μ μ λ ¬μ μννλ€. true λ©΄ μμλ₯Ό λ°κΎΈμ§ μκ³ , false λ©΄ μμλ₯Ό λ°κΎΌλ€.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
μ΄λ₯Ό μν΄ sorted(by:)
λ©μλμ arguments λ‘ μ λ¬ν Closures λ₯Ό λ§λ λ€.
// ascending order(default)
func forward(_ s1: String, _ s2: String) -> Bool {
s1 < s2
}
// descending order
func backward(_ s1: String, _ s2: String) -> Bool {
s1 > s2
}
arguments λ‘ μ λ¬ν
Global Functions
ννμ Closuresforware(_:_:)
,backward(_:_:)
λ₯Ό λ§λ€μλ€.
print("origin : \(names)")
print("default : \(names.sorted())")
print("ascending order : \(names.sorted(by: forward))")
print("descending order: \(names.sorted(by: backward))")
origin : ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
default : ["Alex", "Barry", "Chris", "Daniella", "Ewa"]
ascending order : ["Alex", "Barry", "Chris", "Daniella", "Ewa"]
descending order: ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
2. Closure Expression Syntax
Syntax
{ (parameters) -> return type in
statements
}
Closure Expressions λ λ€μ νΉμ§μ κ°λλ€
Global Functions
μ λ§μ°¬κ°μ§λ‘In-Out Parameters
,Variadic Parameters
,Tuple Type Parameters
,Tuple Type Return
μ΄ νμ©λλ€.- λ¨, Default Parameter Valuesλ νμ©λμ§ μλλ€.
μ μ λ ¬μ Global Functions
ννμ Closures λμ Closure Expressions
ννμ Closures λ₯Ό μ΄μ©νλ©΄ λμ±
κ°κ²°ν ννμ΄ κ°λ₯νλ€.
let ascendingOrderNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 < s2 })
let descendingOrderNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 })
print("ascendingOrderNames : \(ascendingOrderNames)")
print("descendingOrderNames: \(descendingOrderNames)")
ascendingOrderNames : ["Alex", "Barry", "Chris", "Daniella", "Ewa"]
descendingOrderNames: ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
3. Inferring Type From Context
Inline-Closures
λ νμ context
λ‘λΆν° parameter types μ return types λ₯Ό μΆλ‘ (infer)ν μ μμΌλ―λ‘ type
μλ΅μ΄ κ°λ₯νλ€.
(Global Functions μ λ¬λ¦¬ -> return types
λ§μ λ μλ΅ κ°λ₯νλ€)
// ascending order
names.sorted(by: { s1, s2 in return s1 < s2 })
// descending order
names.sorted(by: { s1, s2 in return s1 > s2 })
4. Implicit Return from Single-Expression Closures
ν¨μμ λ§μ°¬κ°μ§λ‘ Single-Expression Closures
λ return
ν€μλλ₯Ό μλ΅ν μ μλ€. μ¬μ€ Global Functions ννμ
Closures κ° κ°λ₯νμΌλ Closure Expressions ννμ Closures μμ κ°λ₯ν κ²μ λΉμ°νλ€.
// ascending order
names.sorted(by: { s1, s2 in s1 < s2 })
// descending order
names.sorted(by: { s1, s2 in s1 > s2 })
5. Shorthand Argument Names
Inline-Closures μ Shorthand Argument Names
λ₯Ό μλμΌλ‘ μ 곡νλ―λ‘, arguments λ₯Ό μλ΅ν μ μμΌλ©°,
μ΄λ $0
, $1
, $2
λ‘ ννλλ€. λν arguments μ μΈ μ체λ₯Ό μλ΅νλ―λ‘ in
ν€μλ μμ νμκ° μμ΄ ν¨κ» μλ΅λμ΄
Closures λ body
λ§μΌλ‘ ꡬμ±λλ€.
// ascending order
names.sorted(by: { $0 < $1 })
// descending order
names.sorted(by: { $0 > $1 })
6. Operator Methods
String
μ greater-than operator(>
)λ₯Ό λ©μλλ‘ κ°λλ€.
λν μ΄ λ©μλλ λ κ°μ parameters λ₯Ό κ°κ³ , Bool μ λ°ννλ€. μ¦, μμμ sorted(by:)
κ° Closures λ‘
λ°λ μ νκ³Ό μ νν μΌμΉνλ€. λ°λΌμ μ΄ κ²½μ° Operator
λ§ λ¨κΈ΄ μ± Shorthand Argument Names λ§μ λ μλ΅ν μ μλ€.
// ascending order
names.sorted(by: <)
// descending order
names.sorted(by: >)
2. Trailing Closures π©βπ»
ν¨μλ₯Ό νΈμΆν λ λ§μ§λ§ parameter
κ° Closure
μ΄κ³ , μ λ¬ λλ argument κ° κΈ΄ ννμμΌ κ²½μ° Trailing Closure
λ₯Ό μ΄μ©ν΄
μ½λμ κ°λ
μ±μ λμΌ μ μλ€. Trailing Closure λ ν¨μλ₯Ό νΈμΆν λ κ΄νΈ(( )
) λ€μμ μμ±νμ§λ§ μ¬μ ν ν¨μμ arguments λ€.
ν¨μλ μ¬λ¬ κ°μ Trailing Closures
λ₯Ό arguments
λ‘ μ
λ ₯ λ°μ μ μμΌλ©°,
첫 λ²μ§Έ Trailing Closure
μ argument labels
λ μλ΅λ μ μλ€ (cf. Multiple Trailing Closures).
1. Trailing Closure Syntax
μλ someFunctionThatTakesAClosure(closure:)
ν¨μλ λ§μ§λ§ arguments λ₯Ό Closures λ‘ κ°λ ν¨μλ€.
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
μ΄μ μ΄ ν¨μλ₯Ό νΈμΆν΄λ³΄μ.
1 ) Trailing Closures μμ΄ ν¨μλ₯Ό νΈμΆ
ν¨μμ argument
λ‘ Closure
λ₯Ό μ λ¬νλ€.
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
2 ) Trailing Closures λ‘ λΆλ¦¬ν΄ ν¨μλ₯Ό νΈμΆ
첫 λ²μ§Έ Trailing Closure
μ΄λ―λ‘ argument labels
λ₯Ό μλ΅ν μ μλ€.
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
μ
Trailing Closures
μ ννλ₯Ό 보면Global Functions
μ κ°λ€λ κ²μ μ μ μλ€.
μ¦,Global Functions
κ° Closures μ νν μ€ νλλΌλ κ²μ νμΈν μ μλ€.
3 ) Trailing Closures κ° ν¨μμ μ μΌν arguments
μΌ λλ κ΄νΈ( )
λ₯Ό μλ΅ν μ μλ€
someFunctionThatTakesAClosure {
// trailing closure's body goes here
}
2. Trailing Closure Examples
1 ) Examples 1: βsorted(by:)β
μ sorted(by:)
λ₯Ό Trailing Closures
λ₯Ό μ΄μ©ν΄ νΈμΆνλ©΄ λ€μκ³Ό κ°λ€.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let descendingNames = names.sorted { $0 > $1 }
print(descendingNames) // ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
Closures λ₯Ό μ λ¬ν΄ μ¬μ©ν λμ Trailing Closures λ₯Ό μ¬μ©ν μ μ°¨μ΄μ λΉκ΅ν΄λ³΄μ.
- Without Trailing Closures
names.sorted(by: { $0 > $1 } )
// Using Operator Methods
names.sorted(by: >)
Closure Expressions λ Operator Methods λ§ λ¨κ²¨ μ΅μ ν ν μ μλ€.
- With Trailing Closures
names.sorted { $0 > $1 }
// Using Operator Methods
print(names.sorted { > }) // error: unary operator cannot be separated from its operand
νμ§λ§ Trailing Closure μ Operator Methods λ§ λ¨κ²¨ μ΅μ ν ν μ μλ€.
2 ) Examples 2: βmap(_:)β
map(_:)
λ©μλλ₯Ό μ΄μ©ν΄ μλ μ£Όμ΄μ§ digitNames, numbers λ°°μ΄λ‘λΆν° μ λ°°μ΄
["OneSix", "FiveEight", "FiveOneZero"]
μ λ°ννλλ‘ Trailing Closures λ₯Ό μ΄μ©ν΄ map(_:)
λ©μλλ₯Ό νΈμΆν΄λ³΄μ.
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map { number -> String in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
print(strings) // ["OneSix", "FiveEight", "FiveOneZero"]
μ Trailing Closures μ Return Types
λ₯Ό μλ΅ν μ μλ€.
let strings = numbers.map { number in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
print(strings) // ["OneSix", "FiveEight", "FiveOneZero"]
λ§μ§λ§μΌλ‘, Shorthand Argument Names
λ₯Ό μ΄μ©ν΄ arguments
μ in
ν€μλ μμ μλ΅ν΄ μ΅μ ν ν μ μλ€.
let strings = numbers.map {
var number = $0
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
print(strings) // ["OneSix", "FiveEight", "FiveOneZero"]
3. A function takes Multiple Trailing Closures
λ§μ½ ν¨μκ° μ¬λ¬ κ°μ Trailing Closures λ₯Ό κ°μ§ κ²½μ°, 첫 λ²μ§Έ Trailing Closure
μ argument labels
λ μλ΅λ μ μλ€.
κ·Έ μΈ λλ¨Έμ§ Trailing Closures λ argument labels μ μ§μ ν΄μΌνλ€.
μλ loadPicture(from:completion:onFailure:)
ν¨μλ₯Ό 보μ.
func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) {
if let picture = download("photo.jpg", from: server) {
completion(picture)
} else {
onFailure()
}
}
loadPicture(from:completion:onFailure:)
λ 3 κ°μ arguments λ₯Ό κ°κ³ μλλ°, 첫 λ²μ§Έλ Server
κ°μ²΄,
λ λ²μ§Έλ (Picture) -> Void
Closure, μΈ λ²μ¬λ() -> Void
Closureλ€.
μ΄μ€ λ λ²μ§Έ arguments μ΄μ 첫 λ²μ§Έ Closure μΈ completion: (Picture) -> Void
λ completion handler
λ‘
λ€μ΄λ‘λκ° μ±κ³΅ν κ²½μ° μ¬μ§μ 보μ¬μ€ κ²μ΄κ³ , μΈ λ²μ§Έ arguments μ΄μ λ λ²μ§Έ Closure μΈ
onFailure: () -> Void
λ error handler
λ‘ μ¬μ©μμκ² μλ¬κ° λ°μνμμ μλ €μ€ κ²μ΄λ€.
μ ν¨μλ₯Ό μ΄λ²μλ Trailing Closures λ₯Ό μ΄μ©ν΄ νΈμΆν΄λ³΄μ.
loadPicture(from: someServer) { picture in
someView.currentPicture = picture
} onFailure: {
print("Couldn't download the next picture.")
}
첫 λ²μ§Έ Trailing Closure μΈ
completion: (Picture) -> Void
λ argument labels λ₯Ό μλ΅νλ€.
νμ§λ§ λ λ²μ§Έ Trailing Closure μΈonFailure: () -> Void
λ argument labels λ₯Ό μλ΅ν μ μμ΄onFailure
λ₯Ό λͺ μνλ€.
loadPicture(from:completion:onFailure:)
μ κ°μ΄ ν¨μλ₯Ό μμ±νλ©΄, νλμ Closure μsuccess
,error
λ₯Ό μ²λ¦¬νλ μ½λλ₯Ό νΌμ¬νμ§ μκ³case
λ³λ‘ μ½λλ₯Ό λͺ ννκ² λΆλ¦¬ν μ μλ€.
μ κ²½μ°λ 2κ°μ completion handlers λ§ μ‘΄μ¬νμ§λ§, μ¬λ¬ κ°μ completion handlers κ° μ€μ²©λλ©΄ μ½λλ₯Ό μ½κΈ° μ΄λ €μμ§λ€. μ΄λ° κ²½μ°
Concurrency
μ μ€λͺ λ κ²μ²λΌ Asynchronous Functions λ₯Ό μ¬μ©ν΄ μ΄λ₯Ό λ체ν μ μλ€.
3. Capturing Values π©βπ»
Closures λ μ μλ λ μ£Όλ³ context μ μμλ λ³μ, μΈμλ₯Ό Capturing
ν΄ μ μ₯ν μ μλ€.
Nested Functions
λ κ°μ₯ κ°λ¨ν ννμ κ° μΊ‘μ²λ‘ context λ΄μ μ΄λ ν constant, variables, arguments λ
μΊ‘μ²ν μ μλ€.
μλ makeIncrementer(forIncrement:)
ν¨μλ₯Ό 보μ.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
ν¨μμ body λ₯Ό μ΄ν΄λ³΄λ©΄, μμ μ μλ Nested Function μΈ incrementer()
λ μ£Όλ³ context μμ λ³μ
runningTotal
κ³Ό νλΌλ―Έν° amount
λ₯Ό μΊ‘μ²ν΄ () -> Int
νμ
μ ν¨μ incrementer()
λ₯Ό λ§λ€μ΄ λ°ννλ€.
μ¦,
makeIncrementer(forIncrement:)
ν¨μλInt
λ₯Ό arguments λ‘ λ°μ() -> Int
νμ μ ν¨μλ₯Ό λ°ννλ€.
λ°νλ ν¨μλ λ€μκ³Ό κ°μ κ²μ΄λ€.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
λ°νλ ν¨μ μ΄λμλ runningTotal
κ³Ό amount
κ° μ μλμ΄μμ§ μμ§λ§ μ΄ ν¨μλ μλνλ€.
κ°μ μΊ‘μ²νκΈ° λλ¬Έμ΄λ€.
μ¦, arguments λ₯Ό λ°μ ν¨μλ₯Ό λ§λ€μ΄λ΄λ ν¨μλ‘ μ¬μ¬μ©μ΄ κ°λ₯νλ€.
let incrementByThree = makeIncrementer(forIncrement: 3)
let incrementByFive = makeIncrementer(forIncrement: 5)
print(incrementByThree()) // 3
print(incrementByThree()) // 6
print(incrementByThree()) // 9
print(incrementByFive()) // 5
print(incrementByFive()) // 10
print(incrementByThree()) // 12
print(incrementByFive()) // 15
incrementByThree()
μincrementByFive()
κ° μλ‘μκ² μν₯μ μ£Όμ§ μκ³ κ°κ° μλν μ μλ μ΄μ λ κ°μ μΊ‘μ²νκΈ° λλ¬Έμ΄λ€.Swift λ κ°μ μΊ‘μ²ν λλ λ¬Όλ‘ μ΄κ³ λ μ΄μ νμνμ§ μμ κ²½μ° μ κ±°νλ κ²κ³Ό κ°μ λͺ¨λ λ©λͺ¨λ¦¬ κ΄λ¦¬λ₯Ό μμμ μ²λ¦¬νλ€.
λμΌν λ‘μ§μ TypeScript λ‘ κ΅¬νν μ½λκ° μλ μλ€. Swift μ λΉκ΅ν΄λ³΄μ.
const makeIncrementer: (forIncrement: number) => () => number
= amount => {
let runningTotal = 0
const incrementer: () => number
= () => {
runningTotal += amount
return runningTotal
}
return incrementer
}
const incrementByThree = makeIncrementer(3)
const incrementByFive = makeIncrementer(5)
console.log(incrementByThree()) // 3
console.log(incrementByThree()) // 6
console.log(incrementByThree()) // 9
console.log(incrementByFive()) // 5
console.log(incrementByFive()) // 10
console.log(incrementByThree()) // 12
console.log(incrementByFive()) // 15
Closure λ₯Ό
Class instance μ Property
μ ν λΉνκ³ , κ·Έ Closure κ° μμ μinstance
λλinstance μ members
λ₯Ό μ°Έμ‘°λ‘ μΊ‘μ²νλ©΄Strong Reference Cycle
(κ°ν μν μ°Έμ‘°)κ° μμ±λλ€.λ°λΌμ Swift λ μ΄ μνμ μ€λ¨μν€κΈ° μν΄
Capture Lists
λ₯Ό μ΄μ©νλ€. ν΄λΉ λ΄μ©μ λ€μμ μ°Έκ³ νλ€. Strong Reference Cycles for Closures
4. Closures Are Reference Types π©βπ»
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
let anotherIncrementByTen = makeIncrementer(forIncrement: 10)
let referToIncrementByTen = incrementByTen
μμμ let
ν€μλλ₯Ό μ΄μ©ν΄ λ€μκ³Ό κ°μ΄ 3κ°μ μμλ₯Ό μ μΈνλ€.
makeIncrementer(forIncrement:)
ν¨μλ₯Ό μ΄μ©ν΄ λ°νλ ν¨μλ₯ΌincrementByTen
μμμ ν λΉνλ€.makeIncrementer(forIncrement:)
ν¨μλ₯Ό μ΄μ©ν΄ λ°νλ ν¨μλ₯ΌanotherIncrementByTen
μμμ ν λΉνλ€.incrementByTen
μμλ₯ΌreferToIncrementByTen
μμμ ν λΉνλ€.
print(incrementByTen()) // 10
print(incrementByTen()) // 20
print(incrementByTen()) // 30
print(anotherIncrementByTen()) // 10
print(referToIncrementByTen()) // 40
print(incrementByTen()) // 50
λͺ¨λ let
ν€μλλ₯Ό μ΄μ©ν΄ μμλ‘ μ μΈνμ§λ§ μΊ‘μ²ν μ¬μ ν runningTotal
λ³μμ κ°μ λ³κ²½λκ³ μλ€. μ κ·Έλ΄κΉ?
incrementByTen()
κ³Ό referToIncrementByTen()
μ μ κ°μ 곡μ ν κΉ?
Functions μ Closures λ
Reference Types
μ΄λ€.let
keyword λ₯Ό μ¬μ©ν΄ μμλ‘ μ μΈλ건incrementByTen
,anotherIncrementByTen
,referToIncrementByTen
ν¨μ μ체λ€. κ·Έλ¦¬κ³ μ΄ ν¨μλvar
keyword λ‘ μ μΈλ λ³μλ₯Ό μΊ‘μ²νκ³ κ°μ λ³κ²½νλ€. κ·Έλ κΈ° λλ¬Έμ λ°ννλ κ°μ΄ λ³κ²½λ μ μλ κ²μ΄λ€.
5. Escaping Closures π©βπ»
ν¨μμ arguments λ‘ μ λ¬λ Closures μ νΈμΆ μμ
μ λ°λΌ λ κ°μ§λ‘ ꡬλΆν μ μλ€.
- ν¨μκ° μ’
λ£λκΈ° μ μ νΈμΆ: ν¨μμ
body λ΄μμ νΈμΆ
λλ€. μΌλ°μ μΌλ‘ μ λ¬λ Closures κ° νΈμΆλλ λ°©μμ΄λ€. - ν¨μκ° μ’
λ£λ ν νΈμΆ:
asynchronous
μ²λ¦¬λ₯Ό μμνλ λλΆλΆμ ν¨μλ arguments λ‘completion handler
λ₯Ό μ¬μ©νλ€. μ΄κ²μ ν¨μκ° μ’ λ£λ ν νΈμΆλλ©°Escaping Closures
λΌ νλ€. μ΄λ₯Ό μν΄ ν¨μλ₯Ό μ μν λ Parameter Types μμ@escaping
ν€μλλ₯Ό μμ±ν΄ μ λ¬λ Closures κ°escape
λ μ μμμ λͺ μν΄μΌνλ€. κ·Έλ μ§ μμΌλ©΄ compile-time error κ° λ°μνλ€.
1. Store in a Variable
Closures λ₯Ό escape μν€λ λ°©λ²μ ν¨μ context μΈλΆ λ³μμ μ μ₯
νλ κ²μ΄λ€.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
completionHandlers.append(completionHandler) // error: converting non-escaping parameter 'completionHandler' to generic parameter 'Element' may allow it to escape
}
parameter λ‘ μ λ¬λ () -> Void
νμ
μ completionHandler
λΌλ μ΄λ¦μ Closures λ₯Ό ν¨μ
context μΈλΆ λ³μμ μ μ₯
νλ € νλ€. μ¦, ν¨μμ body μΈλΆλ‘ escape
μν€λ €λ νμμ΄λ―λ‘ Parameter Types μμ
@escaping
ν€μλλ₯Ό λ°λμ μμ±ν΄μΌνλ€. μ κ²½μ° μ΄λ₯Ό λλ½ν΄ error κ° λ°μνλ€.
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
@escaping
ν€μλλ₯Ό μΆκ°νμ μ μμ μΌλ‘ μλνλ€.
2. Escaping Closures in Classes
Escaping Closures κ° Class Instances μ self
λ₯Ό μ°Έμ‘°νλ κ²½μ° μ£Όμν΄μΌνλ€. self λ₯Ό μΊ‘μ²ν κ²½μ° λ무λ μ½κ²
Strong Reference Cycle
(κ°ν μν μ°Έμ‘°)κ° μκΈ°κΈ° μ½κΈ° λλ¬Έμ΄λ€. Reference Cycles
μ λν΄ μ’ λ μμΈν λ΄μ©μ
Automatic Reference Countingμ μ°Έκ³ νλ€.
λ°λΌμ Closuresλ implicit(μμμ ) μΌλ‘ Closure λ΄λΆ λ³μλ₯Ό μ΄μ©ν΄ μΈλΆ λ³μλ₯Ό μΊ‘μ²
νμ§λ§,
Escaping Closuresλ self
ν€μλ μ΄μ©ν΄ explicit(λͺ
μμ ) μΌλ‘ μ½λλ₯Ό μμ±
νλλ‘νλ€. μ΄λ κ°λ°μμκ² μν μ°Έμ‘°κ° μμμ νμΈνλλ‘ μκΈ°μν¨λ€.
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
Non-Escaping Closures λ₯Ό νλ λ μΆκ°ν΄ μ΄κ²μ΄ self
λ₯Ό μ°Έμ‘°νλ κ²μ΄ μ΄λ»κ² λ€λ₯Έμ§ μ΄ν΄λ³΄μ.
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething() // `someFunctionWithNonescapingClosure` is called in `doSomething` function's body
print(instance.x) // 200
completionHandlers.first?() // `someFunctionWithEscapingClosure ` is not called in `doSomething()` function's body
print(instance.x) // 100
- SomeClass μ instance λ₯Ό μμ±ν΄
instance
μ΄λ¦μ μμμ ν λΉνλ€.- Β
instance
μdoSomething()
μ νΈμΆνλ€. μ΄λsomeFunctionWithEscapingClosure(completionHandler:)
λ@escaping () -> Void
νμ μcompletionHandler
λ₯Ό λ°μcompletionHandlers.append(completionHandler)
λ₯Ό μννλ€. μ¦, μ λ¬λ closure λ₯Ό νΈμΆνμ§ μκ³ μΈλΆμ μ μ₯νλ€.
() -> { self.x = 100 }
μ μΈλΆ completionHandlers μ μ μ₯λλ€.- λ°λ©΄
someFunctionWithNonescapingClosure(closure:))
λ() -> Void
νμ μclosure
λ₯Ό λ°μclosure()
λ₯Ό μννλ€. μ¦, μ λ¬λ closure λ₯Ό μ¦μ νΈμΆνλ€.
() => { x = 200 }
μ μ¦μ νΈμΆλμ΄x
μ 200μ μ μ₯νλ€.instnace
μx
λ₯Ό μΆλ ₯νλ€.200
μ΄ μΆλ ₯λλ€. μ΄λ―Έ μ 2λ²μμsomeFunctionWithNonescapingClosure(closure:))
κ° νΈμΆλμ΄ x μ 200 μ΄λΌλ κ°μ μ μ₯νκΈ° λλ¬Έμ΄λ€.completionHandlers.first?()
λ₯Ό νΈμΆνλ€. μκΉsomeFunctionWithEscapingClosure(completionHandler:)
κ° νΈμΆλλ©° completionHandlers μescaping
λμ΄ μ μ₯ν 첫 λ²μ§Έ closure κ° νΈμΆλλ©° x μ 100 μ μ μ₯νλ€.instnace
μx
λ₯Ό μΆλ ₯νλ€.100
μ΄ μΆλ ₯λλ€. μ 4λ²μμ x μ λ€μ 100 μ μ μ₯νκΈ° λλ¬Έμ΄λ€.
λ€λ₯Έ λ²μ μ doSomething()
ν¨μλ₯Ό 보μ. λ€μμ Escaping Closures
μμλ λ§€λ² self
ν€μλλ₯Ό μ¬μ©νμ§ μκ³ μμμ μΌλ‘
self
λ₯Ό μ°Έμ‘°ν μ μλ€.
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { [self] in x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
self
λ₯Ό μ°Έμ‘°ν μΌμ΄ λ§λ€λ©΄[self]
λ₯Ό μ΄μ©ν΄ implicit νλλ‘ λ§λ€λ©΄ νΈλ¦¬νκ² μΌλ Swift μ μλλλ‘self
ν€μλλ₯Ό νμν κ³³μ μ§μ λͺ μν΄ explicit νλλ‘ μ¬μ©νλ κ²μ΄ λ μμ ν κ² κ°λ€.
3. Escaping Closures in Structures
Structures λ Enumerations μ instance λ νμ self
κ° μμμ implicit
μ΄λ€. λ°λΌμ ν¨μμ parameters
κ°
Structures λ΄λΆμ λ€λ₯Έ Properties μ μ΄λ¦μ΄ λμΌνμ§ μλ ν self
λ₯Ό λͺ
μν νμκ° μλ€.
struct SomeStruct {
var x = 10
mutating func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 } // error: escaping closure captures mutating 'self' parameter
someFunctionWithNonescapingClosure { x = 200 } // Ok
}
}
νμ§λ§ μμ κ°μ μ½λλ compile-time error κ° λ°μλλ€. Value Types
λ κ°μ μ λ’°λ₯Ό 보μ₯νκΈ° μν΄ κΈ°λ³Έμ μΌλ‘ immutable
μμ±μ κ°κΈ° λλ¬Έμ μ΄λ₯Ό λ³κ²½νκΈ° μν΄μλ mutating
ν€μλλ₯Ό μ¬μ©ν context
λ΄λΆμμ λ³κ²½μ΄ μ΄λ£¨μ΄μ ΈμΌνλ€. νμ§λ§
Escaping Closures
λ μ΄ mutating context
κ° μ’
λ£λ ν νΈμΆλκΈ° λλ¬Έμ μ΄λ₯Ό μλ°νλ€.
μλμ κ°μ΄ mutating
ν€μλκ° νμν μ½λλ₯Ό μ μΈνλ©΄ Escaping Closures
λ Value Types
μμλ μ¬μ© κ°λ₯νλ€.
struct SomeStruct {
var x = 10
mutating func doSomething() {
someFunctionWithNonescapingClosure { x = 200 }
}
func anotherDoSomething() {
someFunctionWithEscapingClosure { print("It's OK") }
}
}
var valueTypeInstance = SomeStruct() // It must be declared with `var` not `let`, due to `doSomething()` use mutating member.
valueTypeInstance.doSomething()
print(valueTypeInstance.x) // 200
valueTypeInstance.anotherDoSomething()
completionHandlers.first?() // It's OK
Value Types
μμEscaping Closures
λmutating
μ ν μ μλ€. μ΄κ²μ΄ Value Types μμ Escaping Closures κ° μ¬μ© λΆκ°λ₯ν¨μ μλ―Ένλ κ²μ μλλ€. μ¬μ ν mutating μ΄ νμνμ§ μμ closures λ escaping λ μ μλ€.
6. Autoclosures π©βπ»
ν¨μμ arguments λ‘ closures λ₯Ό μ λ¬ν λ μ΄ closure κ° arguments λ₯Ό κ°μ§ μκ³ expressions μ κ²°κ³Όλ₯Ό λ°ν
νλ
κ²½μ° { arguments in expressions }
μμ arguments μ in μ λ¬Όλ‘ μ΄κ³ { } λ§μ λ μλ΅νκ³ expressions
λ§μΌλ‘
μμ±νλλ‘ ν μ μλ€.
μ΄λ¬ν closure λ₯Ό Autoclosure
λΌ νλ©°, ν¨μλ₯Ό μ μν λ @autoclosure
ν€μλλ₯Ό μΆκ°ν¨μΌλ‘μ¨ μ μν μ μλ€. Swift
λ μ΄λ κ² μ λ¬λ expressions
μ μλμΌλ‘ Closures ννλ‘ wrapping νλ€.
Autoclosures
λ μ½λκ° νΈμΆλ λ κΉμ§ μ€νλμ§ μκΈ° λλ¬Έμ νκ°λ₯Ό μ§μ°
ν μ μλ€. λ°λΌμ side effects
κ°
μκ±°λ μκ°μ΄ μ€λ 걸리λcomputationally expensive
μ½λμ μ μ©νλ€.
1. Closures Evaluated when Called
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let returned = customersInLine.remove(at: 0)
print(returned) // Chris
Arrays μ remove(at:)
λ©μλλ μ£Όμ΄μ§ index μ λ°°μ΄μ μ κ±° ν κ·Έ κ°μ λ°ννλ€.
let customerProvider = { customersInLine.remove(at: 0) }
λ°λΌμ μμ κ°μ΄ μ μλ customerProvider
λ () -> String
νμ
μ΄λ€.
λ€μμ Closures κ° μ½λ μ€νμ μ΄λ»κ² μ§μ°μν€λμ§λ₯Ό 보μ¬μ€λ€.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine) // ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count) // 5
customerProvider()
print(customersInLine) // ["Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count) // 4
customerProvider()
print(customersInLine) // ["Ewa", "Barry", "Daniella"]
print(customersInLine.count) // 3
μ μ½λ λΈλμ 보면 let customerProvider = { customersInLine.remove(at: 0) }
λΌμΈ μ΄ν customersInLine
λ₯Ό
μΆλ ₯ν΄λ³΄λ©΄ κ·Έλλ‘μΈ κ²μ μ μ μλ€. μ΄ν customerProvider()
κ° νΈμΆλ λ μ κ±°λκ³ κ·Έ κ°μ λ°ννλ€.
2. Autoclosure Type Parameters
1 ) Parameter Type is a Closure
μ΄λ¬ν μ½λ μ§μ°μ ν¨μμ arguments λ‘ Closures λ₯Ό μ λ¬ν λλ λμΌν μλμ κ°λλ€.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) }) // Now serving Chris!
let customersInLine: string[] = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
function serve(customerProvider: () => string) {
console.log(`Now serving ${customerProvider()}!`)
}
serve(() => customersInLine.shift()!) // Now serving Chris!
Swift μ λ¬λ¦¬ arguments μ체λ₯Ό μλ΅ ν μλ μκ³ λΉμμ 보λ΄μΌνλ€.
2 ) Parameter Type is an Autoclosure
μ΄λ Parameter Types μμ @autoclosure
ν€μλλ₯Ό λΆμ¬μ£Όλ©΄ arguments λ‘ μ λ¬λ expressions
λ₯Ό μ»΄νμΌλ¬κ° μλμΌλ‘
Closures ννλ‘ wrapping νλ€.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
// `{ }` can be omitted
serve(customer: customersInLine.remove(at: 0)) // Now serving Chris!
Autoclosures
λ₯Ό μ¬μ©ν΄ parameters λ‘ closures κ° μλ String μ λ°λ κ²μ²λΌ μ¬μ©ν μ μλ€.
TypeScript μλ μ΄μ μ μ¬ν λ¬Έλ²μ΄ μ‘΄μ¬νμ§ μλλ€. Swift μΈμ΄μλ§ μ‘΄μ¬νλ λ¬Έλ²μ΄λ€.
3 ) Parameter Type is a String
μ 2λ²μ parameter λ‘ String μ λ°λ κ²μ²λΌ 보μ΄μ§λ§ μ¬μ ν closure λ₯Ό λ°κ³ μλ€. λ€μ μ½λλ Autoclosures
μμ΄
μ λ§λ‘ String μ parameter λ‘ λ°λ ν¨μλ€.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: String) {
print("Now serving \(customerProvider)!")
}
serve(customer: customersInLine.remove(at: 0)) // Now serving Chris!
let customersInLine: string[] = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
function serve(customerProvider: string) {
console.log(`Now serving ${customerProvider}!`)
}
serve(customersInLine.shift()!)
μ 1 ~ 3μ λͺ¨λ λμΌν λ‘μ§μ μνν μ μλ€. λ¨, μ΄λ€μ μ°¨μ΄μ μ μ μ΄ν΄νκ³ μ¬μ©ν΄μΌνλ€.
- parameters λ‘
Closure
λ₯Ό λ°λλ€.- parameters λ‘
Autoclosures
λ₯Ό λ°λλ€. κ²λ³΄κΈ°μλexpressions
μ κ²°κ³Όλ‘ λ¨μType
μ λ°λ κ²μ²λΌ λ³΄μΌ μ μμ§λ§ μ¬μ νClosure
λ₯Ό parameters λ‘ λ°λλ€.- parameters λ‘
expressions
μ μν ν κ·Έ κ²°κ³Όλ₯Ό λ°λλ€. μ 2λ²κ³Ό λ¬λ¦¬ μ λ§λ‘ κ²°κ³ΌμType
μ parameters λ‘ λ°λλ€.
Autoclosures
λ₯Ό μ¬μ©ν¨μΌλ‘μ¨ λ¨μν κ·ΈTypes
μ parameters λ‘ λ°λ κ²μ²λΌ κ°λ μ±μ λμΌ μ μμΌλ λ¨μ©ν κ²½μ° μ€νλ € μ½λλ₯Ό μ΄ν΄νκΈ° μ΄λ ΅κ² λ§λ λ€. μ΄λκΉμ§λ μ΄κ²μ 3λ² μ²λΌ κ²°κ³ΌμType
μ΄ μλ 1λ² μ²λΌClosure
λ₯Ό λ°λ κ²μ΄κΈ° λλ¬Έμ΄λ€. λ°λΌμAutoclosures
λ₯Ό μ¬μ©ν λ context μ ν¨μμ μ΄λ¦μ μ½λμ μ€νμ΄ μ§μ°λ¨μ λΆλͺ ν ν΄μΌνλ€.
3. Autoclosures with Escaping Closures
@autoclosure
κ³Ό @escaping
μ ν¨κ» μ¬μ©ν μ μλ€.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
print("customerProviders: \(customerProviders)")
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
Collected 2 closures.
[(Function), (Function)]
Now serving Chris!
Now serving Alex!
μμμλ μ€λͺ νμ§λ§,
Autoclosures
μ λ¨μ©μ μ½λλ₯Ό μ΄ν΄νκΈ° μ΄λ ΅κ² λ§λ λ€.
7. Compare with TypeScript π©βπ»
μ΄ κΈμ μμ λΆλΆμμ Swift μ Closures λ λ€λ₯Έ μΈμ΄μ Lambda expressions
μ λΉμ·νλ€κ³ νλ€.
λ€μ ν λ² μ 리νλ©΄μ λ§λ¬΄λ¦¬ν΄λ³΄μ.
1. Function Declarations
with function
/ func
keyword
function sum(a: number, b: number): number {
return a + b
}
console.log(sum(5, 7)) // 12
func sum(_ a: Int, _ b: Int) -> Int {
a + b
}
print(sum(5, 7)) // 12
2. Function Expressions
with Lambda expressions
/ Closures
- Implicit Type Inference
// Function Expressions with lambda expressions
const product = (a: number, b: number) => a * b
console.log(product(5, 7)) // 35
// Function Expressions with closures
let product = { (a: Int, b: Int) in
a * b
}
print(product(5, 7)) // 35
Return Type μ΄ μλ΅λμλ€.
- Explicit Type Declaration
const product = (a: number, b: number): number => a * b
console.log(product(5, 7)) // 35
let product = { (a: Int, b: Int) -> Int in
a * b
}
print(product(5, 7)) // 35
λλ
const product: (num1: number, num2: number) => number = (a, b) => a * b
console.log(product(5, 7)) // 35
let product: (Int, Int) -> Int = { $0 * $1 }
print(product(5, 7)) // 35
3. Lambda expressions
/ Closures
// Function Expressions omit { }
const greetingMessage = () => "Hello~ TypeScript"
console.log(greetingMessage()) // Hello~ TypeScript
let greetingMessage = { "Hello~ Swift" }
print(greetingMessage()) // Hello~ Swift
4. Autoclosures
let customersInLine: string[] = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
function serve(customerProvider: () => string) {
console.log(`Now serving ${customerProvider()}!`)
}
serve(() => customersInLine.shift()!) // Now serving Chris!
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
// `{ }` can be omitted
serve(customer: customersInLine.remove(at: 0)) // Now serving Chris!
TypeScript μμ
serve(() => { return customersInLine.shift()! // Now serving Chris! })
λ₯Ό
serve(() => customersInLine.shift()!)
λ‘
{ }
μreturn
ν€μλλ₯Ό μλ΅ν μ μμΌλ Swift μ λ¬λ¦¬arguments
μ체λ₯Ό μλ΅ν μλ μμ΄Autoclosures
μ²λΌ μμ ν νλ¬Έμ μ λ―μ΄ μμ±νλ κ²μ λΆκ°λ₯νλ€. μ¦ Swift μAutoclosures
κ°λ μ μμ ν μΌμΉνλ λ¬Έλ²μ μλ€.λ¨, μ΄ κ²½μ°λ
Closure
κ° ν¨μμarguments
λ‘ μ λ¬λ λAutoclosures
μ μν΄{ }
κ° μλ΅λλ κ²μΌ λΏμ΄λ€.
ν¨μμarguments
κ° μλ μΌλ°μ μΈClosures
λ₯Ό μ μΈν λλ₯Ό λΉκ΅ν΄λ³΄λ©΄const product = (a: number, b: number) => a * b
let product = { (a: Int, b: Int) in a * b }
TypeScript λ νΉμ μ‘°κ±΄μ΄ λ§μ‘±ν κ²½μ°
Closures
μBody
λ₯Ό κ°μΈλ{ }
λ₯Ό μλ΅ν μ μλ κ²κ³Ό λ¬λ¦¬ Swift λarguments
λ₯Ό μλ΅ν μ μμΌλ μμ κ°μ΄Autoclosures
κ° μλ κ²½μ°λ{ }
λ₯Ό μλ΅ν μ μλ€.μ¦, μ΄κ²μ μ΄λ€ μΈμ΄κ° λ μ°μνλ€λ κ²μ΄ μλ μΈμ΄μ λ°λ₯Έ νΉμ±μ΄λ μ΄λ₯Ό μ μ΄ν΄νκ³ μ¬μ©νλ κ²μ΄ νμνλ€.
5. Capturing Values with Closures
Functions
λ Closures
μ νΉλ³ν ννλ‘ μ 1~3λ² λͺ¨λ Closures
λ€. λν Swift μ Closures
λ λ€λ₯Έ μΈμ΄μ
Lambda expressions
μ μ μ¬νλ€. Swift λΏ μλλΌ ν¨μλ₯Ό First-Class Citizens
λ‘ μ·¨κΈνλ ν¨μν νλ‘κ·Έλλ° μΈμ΄λ
μ΄μ μ μ¬ν νΉμ±μ κ°λλ€κ³ 보면 λλ€.
TypeScript μ κ²½μ° μΌλ°μ μΌλ‘ Closures λ₯Ό μ€λͺ
ν λ κ°μ μΊ‘μ²
νκ³ , return type
μ΄ ν¨μμΈ κ²½μ°. μ¦, ν¨μλ₯Ό
μ΄μ©ν΄ ν¨μλ₯Ό λ§λ€μ΄ λ°ννλ ν¨μλ‘μ¨ Closures λ₯Ό μ€λͺ
νλ κ²½μ°κ° λ§μλ° μ΄κ² μμ First-Class Citizens
μ νΉμ±μ΄λ―λ‘
λ μΈμ΄ λͺ¨λ λμΌν λ‘μ§μ ꡬνν μ μλ€.
const makeIncrementer: (forIncrement: number) => () => number
= amount => {
let runningTotal = 0
const incrementer: () => number
= () => {
runningTotal += amount
return runningTotal
}
return incrementer
}
const increaseByThree = makeIncrementer(3)
console.log(increaseByThree()) // 3
console.log(increaseByThree()) // 6
console.log(increaseByThree()) // 9
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let increaseByThree = makeIncrementer(forIncrement: 3)
print(increaseByThree()) // 3
print(increaseByThree()) // 6
print(increaseByThree()) // 9
Reference
- βClosures.β The Swift Programming Language Swift 5.7. accessed Oct. 24, 2022, Swift Docs Chapter 5 - Closures.