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 ν˜•νƒœμ˜ Closures forware(_:_:), 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 의 호좜 μ‹œμ μ— 따라 두 κ°€μ§€λ‘œ ꡬ뢄할 수 μžˆλ‹€.

  1. ν•¨μˆ˜κ°€ μ’…λ£Œλ˜κΈ° 전에 호좜: ν•¨μˆ˜μ˜ body λ‚΄μ—μ„œ ν˜ΈμΆœλœλ‹€. 일반적으둜 μ „λ‹¬λœ Closures κ°€ ν˜ΈμΆœλ˜λŠ” 방식이닀.
  2. ν•¨μˆ˜κ°€ μ’…λ£Œλœ ν›„ 호좜: 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
  1. SomeClass 의 instance λ₯Ό 생성해 instance μ΄λ¦„μ˜ μƒμˆ˜μ— ν• λ‹Ήν•œλ‹€.
  2. Β 
    • 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을 μ €μž₯ν•œλ‹€.

  3. instnace의 xλ₯Ό 좜λ ₯ν•œλ‹€. 200이 좜λ ₯λœλ‹€. 이미 μœ„ 2λ²ˆμ—μ„œ someFunctionWithNonescapingClosure(closure:))κ°€ ν˜ΈμΆœλ˜μ–΄ x 에 200 μ΄λΌλŠ” 값을 μ €μž₯ν–ˆκΈ° λ•Œλ¬Έμ΄λ‹€.
  4. completionHandlers.first?()λ₯Ό ν˜ΈμΆœν•œλ‹€. μ•„κΉŒ someFunctionWithEscapingClosure(completionHandler:)κ°€ 호좜되며 completionHandlers 에 escaping λ˜μ–΄ μ €μž₯ν•œ 첫 번째 closure κ°€ 호좜되며 x 에 100 을 μ €μž₯ν•œλ‹€.
  5. 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은 λͺ¨λ‘ λ™μΌν•œ λ‘œμ§μ„ μˆ˜ν–‰ν•  수 μžˆλ‹€. 단, μ΄λ“€μ˜ 차이점을 잘 μ΄ν•΄ν•˜κ³  μ‚¬μš©ν•΄μ•Όν•œλ‹€.

  1. parameters 둜 Closure λ₯Ό λ°›λŠ”λ‹€.
  2. parameters 둜 Autoclosures λ₯Ό λ°›λŠ”λ‹€. κ²‰λ³΄κΈ°μ—λŠ” expressions의 결과둜 λ‹¨μˆœ Type을 λ°›λŠ” κ²ƒμ²˜λŸΌ 보일 수 μžˆμ§€λ§Œ μ—¬μ „νžˆ Closure λ₯Ό parameters 둜 λ°›λŠ”λ‹€.
  3. 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

  1. β€œClosures.” The Swift Programming Language Swift 5.7. accessed Oct. 24, 2022, Swift Docs Chapter 5 - Closures.