ν΄λ‘œμ €(Closure)

λ¦°λ‹€Β·2022λ…„ 2μ›” 16일
0

The Swift Language Guide

λͺ©λ‘ 보기
1/7
post-thumbnail

μ•„λž˜μ˜ 글은 The swift programming languageκ³Ό bbigudukλ‹˜, jusungλ‹˜μ˜ λ²ˆμ—­λ³Έμ„ 보며 κ³΅λΆ€ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.

βœ”οΈΒ μ •μ˜

  • 자쑱 κ°€λŠ₯ν•œ ν•¨μˆ˜ λΈ”λŸ­

πŸ“šΒ νŠΉμ§•

  • μ „λ‹¬λ˜λŠ” 것도 κ°€λŠ₯, μ½”λ“œ λ‚΄λΆ€μ—μ„œ μ‹€ν–‰λ˜λŠ” 것도 κ°€λŠ₯
  • μƒμˆ˜μ™€ λ³€μˆ˜μ— λŒ€ν•΄ μ°Έμ‘°λ₯Ό μΊ‘μ³ν•˜κ³  μ €μž₯ν•  수 있음(closing over)
  • λ§₯락 상 νŒŒλΌλ―Έν„°μ™€ λ°˜ν™˜κ°’ νƒ€μž… μœ μΆ”
  • 단일 ν‘œν˜„μ‹ ν΄λ‘œμ €μ˜ μ•”μ‹œμ  λ°˜ν™˜(return ν‚€μ›Œλ“œμ˜ μƒλž΅)
  • 약식 인자 이름($0)
  • ν›„ν–‰ ν΄λ‘œμ € ꡬ문

❓쒅λ₯˜

  • Global Function(μ „μ—­ν•¨μˆ˜): 이름 O, μ–΄λ– ν•œ 값도 μΊ‘μ³ν•˜μ§€ μ•ŠλŠ” ν΄λ‘œμ €
  • Nested Function(μ€‘μ²©ν•¨μˆ˜): 이름 O, 값을 μΊ‘μ²˜ν•˜λŠ” ν΄λ‘œμ €
  • Closure Expressions(ν΄λ‘œμ € ν‘œν˜„μ‹), Unnamed Closures: 이름 X, 값을 μΊ‘μ²˜ν•˜λŠ” ν΄λ‘œμ €

Clsoure Expressions

  • 짧고 λͺ…λ£Œν•˜κ²Œ inline closureλ₯Ό μž‘μ„±ν•˜λŠ” 방법

Ex. sorted(by:)

  • sorted(by:) λ©”μ„œλ“œλŠ” 인자둜 ν΄λ‘œμ €λ₯Ό ν—ˆμš©ν•˜κ³  있음
  • μ΄λ•Œ ν΄λ‘œμ €μ˜ νƒ€μž…μ€ λ°°μ—΄κ³Ό λ™μΌν•œ νƒ€μž…μ˜ 인자 두 개λ₯Ό λ°›κ³  μ˜€λ¦„μ°¨μˆœμΈμ§€ λ‚΄λ¦Όμ°¨μˆœμΈμ§€ νŒλ‹¨ν•  수 μžˆλŠ” Bool값을 λ¦¬ν„΄ν•˜κ³  있음
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

// 이런 κ²½μš°μ— μ •λ ¬ ν΄λ‘œμ €λŠ” (String, String) -> Bool νƒ€μž…
  1. ν•„μš”ν•œ νƒ€μž…μ˜ 일반 ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜κ³  sorted(by:) λ©”μ„œλ“œμ— 인자둜 μ „λ‹¬ν•˜κΈ°
func backward(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
  1. inline closure λ°©μ‹μœΌλ‘œ μž‘μ„±ν•˜κΈ°

Untitled

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

// in ν‚€μ›Œλ“œλ‘œ ν΄λ‘œμ €μ˜ νŒŒλΌλ―Έν„°, 리턴 νƒ€μž… μ •μ˜κ°€ 끝났고 이 λ’€λΆ€ν„° bodyκ°€ μ‹œμž‘λ¨μ„ μ•Œλ €μ€Œ
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
  1. λ§₯락 상 νƒ€μž… μœ μΆ”
  • ν•¨μˆ˜, λ©”μ„œλ“œμ— ν΄λ‘œμ €λ₯Ό inline closure ν‘œν˜„μ‹μœΌλ‘œ μ „λ‹¬ν•˜λ©΄ 항상 νŒŒλΌλ―Έν„° νƒ€μž…κ³Ό λ°˜ν™˜ νƒ€μž…μ„ μœ μΆ”ν•  수 있음
  • ν΄λ‘œμ €κ°€ ν•¨μˆ˜ 인자둜 μ‚¬μš©λ  λ•Œ μ™„μ „ν•œ ν˜•νƒœλ‘œ μž‘μ„±ν•  ν•„μš”κ°€ μ—†λ‹€λŠ” 뜻!!
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
  1. 단일 ν‘œν˜„ ν΄λ‘œμ €μ˜ μ•”μ‹œμ  λ°˜ν™˜(Implicit Returns from Single-Expression Closures)
  • ν΄λ‘œμ €κ°€ ν•œ μ€„λ‘œ ν‘œν˜„λΌμžˆλŠ” 경우 return ν‚€μ›Œλ“œ μƒλž΅μ΄ κ°€λŠ₯
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
  1. 짧은 인자 이름 (Shorthand Argument Names)
reversedNames = names.sorted(by: { $0 > $1 } )
  1. μ—°μ‚°μž λ©”μ„œλ“œ (Operator Methods)
reversedNames = names.sorted(by: >)

⛏ 후행 ν΄λ‘œμ €(Trailing Closure)

  • ν•¨μˆ˜μ˜ λ§ˆμ§€λ§‰ 인자둜 ν•¨μˆ˜μ— ν΄λ‘œμ €λ₯Ό μ „λ‹¬ν•΄μ•Όν•˜κ±°λ‚˜, ν΄λ‘œμ € ν‘œν˜„μ‹μ΄ κΈ΄ 경우 주둜 μ‚¬μš©ν•¨
  • ν•¨μˆ˜μ˜ μΈμžμ΄μ§€λ§Œ ν•¨μˆ˜ 호좜의 μ†Œκ΄„ν˜Έ 이후에 μž‘μ„±ν•¨
func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}

// Here's how you call this function without using a trailing closure:

someFunctionThatTakesAClosure(closure: {
    // closure's body goes here
})

// Here's how you call this function with a trailing closure instead:

someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}
reversedNames = names.sorted() { $0 > $1 }
  • λ§Œμ•½μ— ν΄λ‘œμ €κ°€ μœ μΌν•œ 인자의 경우, ν•¨μˆ˜ ν˜ΈμΆœμ‹œ μ†Œκ΄„ν˜Έλ„ μƒλž΅κ°€λŠ₯함
reversedNames = names.sorted { $0 > $1 }
  • ν΄λ‘œμ €κ°€ κΈΈμ–΄μ„œ ν•œμ€„λ‘œ 인라인이 λΆˆκ°€λŠ₯ ν•  λ•Œ ν›„ν–‰ ν΄λ‘œμ €λ₯Ό μœ μš©ν•˜κ²Œ μ‚¬μš©ν•  수 있음
// Int -> String으둜 λ°˜ν™˜ν•˜κΈ° μœ„ν•΄ 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]
// λ°°μ—΄μ˜ κ°’μ—μ„œ νƒ€μž…μ„ μœ μΆ”ν•  수 있기 λ•Œλ¬Έμ— number νƒ€μž… μ–ΈκΈ‰ν•„μš”x
let strings = numbers.map { (number) -> String in
		// ν•΄λ‹Ή numberλŠ” μˆ˜μ • κ°€λŠ₯(νŒŒλΌλ―Έν„°λŠ” 늘 μƒμˆ˜)
    var number = number
    var output = ""
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]
  • ν•¨μˆ˜κ°€ μ—¬λŸ¬ 개의 ν΄λ‘œμ €λ₯Ό 가지고 μžˆλ‹€λ©΄ 첫번째 ν›„ν–‰ ν΄λ‘œμ €μ˜ argument label은 μƒλž΅, 남은 ν›„ν–‰ ν΄λ‘œμ €λ§Œ ν‘œκΈ°
func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) {
    if let picture = download("photo.jpg", from: server) {
        completion(picture)
    } else {
        onFailure()
    }
}
loadPicture(from: someServer) { picture in
    someView.currentPicture = picture
} onFailure: {
    print("Couldn't download the next picture.")
}

πŸ’‘Β μΊ‘μ²˜κ°’(Capturing Values)

  • ν΄λ‘œμ €λŠ” μ„ μ–Έλœ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μƒμˆ˜, λ³€μˆ˜λ₯Ό μΊ‘μ²˜ν•  수 있음
  • 이λ₯Ό 톡해 μƒμˆ˜, λ³€μˆ˜κ°€ μ •μ˜λœ λ²”μœ„κ°€ 더이상 μ‘΄μž¬ν•˜μ§€ μ•Šλ”λΌλ„ body λ‚΄λΆ€μ—μ„œ ν•΄λ‹Ή μƒμˆ˜, λ³€μˆ˜μ˜ 값을 μ°Έμ‘°ν•˜κ³  μˆ˜μ •ν•  수 있음
  • λŒ€ν‘œμ μΈ μ˜ˆμ‹œ: λ‹€λ₯Έ ν•¨μˆ˜μ˜ body 내뢀에 μž‘μ„±ν•˜λŠ” 쀑첩 ν•¨μˆ˜
    • μ€‘μ²©ν•¨μˆ˜λŠ” λ°”κΉ₯ ν•¨μˆ˜μ˜ 인자, λ°”κΉ₯ ν•¨μˆ˜ 내에 μ •μ˜λœ μƒμˆ˜, λ³€μˆ˜λ„ μΊ‘μ²˜ν•  수 있음
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
		// incrementer()λŠ” runningTotal, amount μΊ‘μ²˜ν•¨
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}
  • makeIncrementer의 λ°˜ν™˜νƒ€μž…μ€ () β†’ Int, ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜κ³  있음
func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}
  • μ΄λ ‡κ²Œ λ‹¨λ…μœΌλ‘œ 보면 비정상적인 ν•¨μˆ˜μ²˜λŸΌ 보일 수 있음
  • incrementer() ν•¨μˆ˜λŠ” νŒŒλΌλ―Έν„°κ°€ μ—†κΈ° λ•Œλ¬Έμ—...ν•˜μ§€λ§Œ runningTotal, amountλ₯Ό μΊ‘μ²˜ν•˜κ³  있음
  • μ°Έμ‘°λ₯Ό μΊ‘μ²˜ν•œλ‹€ = makeIncrementer ν•¨μˆ˜ 호좜이 μ’…λ£Œλ  λ•Œ runningTotal, amountκ°€ μ‚¬λΌμ§€μ§€μ•Šκ³  λ‹€μŒ Incrementer ν•¨μˆ˜κ°€ 호좜될 λ•Œ runningTotal을 μ‚¬μš©ν•  수 있음
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
  • μƒˆλ‘œ μƒμ„±λœ makeIncrementer ν•¨μˆ˜λŠ” μƒˆλ‘œ λΆ„λ¦¬λœ runningTotal λ³€μˆ˜μ— μ°Έμ‘° μ €μž₯됨
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
incrementByTen()
// returns a value of 40
// κΈ°μ‘΄ λ³€μˆ˜μ— 영ν–₯을 주지 μ•ŠμŒ

πŸ“‚Β μ°Έμ‘°νƒ€μž…

  • μœ„μ—μ„œ incrementBySeven, incrementByTen은 let으둜 μ„ μ–Έλœ μƒμˆ˜μ΄μ§€λ§Œ ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•  λ•Œ λ§ˆλ‹€ return 값이 λ³€ν•˜κ³  있음
  • μ΄λŠ” ν΄λ‘œμ €κ°€ μ°Έμ‘° νƒ€μž…μ΄κΈ° λ•Œλ¬Έμ— κ°€λŠ₯함
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50

incrementByTen()
// returns a value of 60

Escaping Closure(νƒˆμΆœ ν΄λ‘œμ €)

  • ν•¨μˆ˜κ°€ λ°˜ν™˜λœ ν›„, 호좜이 μ’…λ£Œλœ 후에 ν˜ΈμΆœλ˜λŠ” ν΄λ‘œμ €
  • ν‚€μ›Œλ“œ @escaping
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
  • μ΄λ•Œ esacping closure 쀑 selfκ°€ 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μ°Έμ‘°ν•˜λŠ” κ²½μš°μ—λŠ” μ£Όμ˜ν•΄μ•Όν•¨ β†’ κ°•ν•œ μ°Έμ‘° 사이클 생길 수 있기 λ•Œλ¬Έμ—
func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
				// escaping ν΄λ‘œμ €μ˜ 경우, ν•¨μˆ˜κ°€ 호좜된 후에 μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ— ν•¨μˆ˜μ˜ ν”„λ‘œνΌν‹°μΈ xμž„μ„ λ‚˜νƒ€λ‚΄κΈ° μœ„ν•΄μ„œ selfκ°€ ν•„μš”ν•¨
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"

completionHandlers.first?()
print(instance.x)
// Prints "100"
  • selfλ₯Ό μΊ‘μ²˜ν•˜κ³  μ•”μ‹œμ μœΌλ‘œ μ°Έμ‘°ν•˜λŠ” 것도 κ°€λŠ₯
class SomeOtherClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { [self] in x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}
  • λ§Œμ•½μ— selfκ°€ struct, enumeration의 μΈμŠ€ν„΄μŠ€λ©΄ 항상 μ•”μ‹œμ μœΌλ‘œ self μ°Έμ‘°κ°€ κ°€λŠ₯함(κ°’νƒ€μž…)
  • ν•˜μ§€λ§Œ escaping closureλŠ” κ°’νƒ€μž…μΈ selfλ₯Ό λ³€κ²½ κ°€λŠ₯ν•œ μ°Έμ‘°(mutating)둜 μΊ‘μ²˜ν•  수 μ—†μŒ
struct SomeStruct {
    var x = 10
    mutating func doSomething() {
        someFunctionWithNonescapingClosure { x = 200 }  // Ok
        someFunctionWithEscapingClosure { x = 100 }     // Error
    }
}

Autoclosure

  • ν•¨μˆ˜μ— 인자둜 μ „λ‹¬λ˜λŠ” ν‘œν˜„μ‹μ„ λž˜ν•‘ν•˜κΈ° μœ„ν•΄ μžλ™μœΌλ‘œ μƒμ„±λ˜λŠ” ν΄λ‘œμ €
  • 인자λ₯Ό 가지지 μ•ŠμœΌλ©° 호좜될 λ•Œ 내뢀에 λž˜ν•‘λœ ν‘œν˜„μ‹μ˜ 값을 λ°˜ν™˜
  • ν•¨μˆ˜λ₯Ό μ„ μ–Έν•œ ν›„ ν˜ΈμΆœν•˜κΈ° μ „κΉŒμ§€λŠ” μ‹€ν–‰λ˜μ§€ μ•ŠμŒ(μ§€μ—°νš¨κ³Ό)
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"

// () -> String νƒ€μž…μ„ 가짐
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"

// ν• λ‹Ή ν›„ μ‹€ν–‰μ‹œμΌœμ•Όν•¨
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
  • ν•¨μˆ˜μ˜ 인자둜 ν΄λ‘œμ €λ₯Ό 전달해도 같은 효과λ₯Ό λ³Ό 수 있음
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
  • νŒŒλΌλ―Έν„°μ— @autoclosureλ₯Ό μž‘μ„±ν•˜λ©΄ ν΄λ‘œμ € λŒ€μ‹  string을 λ°›λŠ” 것 처럼 μž‘μ„±ν•  수 있음
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"
  • μžλ™ ν΄λ‘œμ €μ™€ μ΄μŠ€μΌ€μ΄ν•‘μ€ λ™μ‹œμ— μ‚¬μš©ν•  수 있음
// customersInLine is ["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.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
    print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"

0개의 λŒ“κΈ€