λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
⌨️ Language/swift

[Swift] ν΄λ‘œμ €

by hyebin (Helia) 2024. 3. 18.
λ°˜μ‘ν˜•

ν΄λ‘œμ €(Closures)

μ–΄λ–€ μƒμˆ˜λ‚˜ λ³€μˆ˜μ˜ μ°Έμ‘°λ₯Ό 캑처(κ°’μ˜ μ°Έμ‘°λ₯Ό κ°–λŠ”λ‹€.)ν•΄ μ €μž₯ν•  수 μžˆλŠ” μ½”λ“œ λΈ”λŸ­

SwiftλŠ” κ΄€λ ¨λœ λ©”λͺ¨λ¦¬λ₯Ό μ•Œμ•„μ„œ 처리

 

ν΄λ‘œμ €μ˜ ν˜•νƒœ

  • μ „μ—­ ν•¨μˆ˜ : 이름이 있고 μ–΄λ–€ 값도 μΊ‘μ²˜ν•˜μ§€ μ•ŠλŠ” ν΄λ‘œμ €
  • 쀑첩 ν•¨μˆ˜ : 이름이 있고 κ΄€λ ¨ν•œ ν•¨μˆ˜(ν•˜μœ„→μƒμœ„)둜 λΆ€ν„° 값을 캑처 ν•  수 μžˆλŠ” ν΄λ‘œμ €
  • ν΄λ‘œμ € ν‘œν˜„ : κ²½λŸ‰ν™” 된 λ¬Έλ²•μœΌλ‘œ μ“°μ—¬μ§€κ³  κ΄€λ ¨λœ λ¬Έλ§₯(context)μœΌλ‘œλΆ€ν„° 값을 캑쳐할 수 μžˆλŠ” 이름이 μ—†λŠ” ν΄λ‘œμ €

 

ν΄λ‘œμ € ν‘œν˜„ (Closure Expressions)

인라인 ν΄λ‘œμ €λ₯Ό λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•˜λŠ” λ°©λ²•μœΌλ‘œ 문법에 초점이 맞좰져있음

μ½”λ“œμ˜ λͺ…ν™•μ„±κ³Ό μ˜λ„λ₯Ό μžƒμ§€ μ•ŠμœΌλ©΄μ„œλ„ 문법을 μΆ•μ•½ν•΄ μ‚¬μš©ν•  수 μžˆλŠ” λ‹€μ–‘ν•œ λ¬Έλ²•μ˜ μ΅œμ ν™” 방법을 제곡

 

μ •λ ¬ λ©”μ†Œλ“œ(The Sorted Method)

Swift ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ sorted(by:)λΌλŠ” νƒ€μž…μ˜ λ°°μ—΄ 값을 μ •λ ¬ν•˜λŠ” λ©”μ†Œλ“œλ₯Ό 제곡

by에 μ •λ ¬ 방법을 μž‘μ„±ν•œ ν΄λ‘œμ €λ₯Ό λ„£μœΌλ©΄ κ·Έ λ°©λ²•λŒ€λ‘œ μ •λ ¬λœ 배열을 얻을 수 있음

sorted(by:)λ©”μ†Œλ“œλŠ” 원본 배열은 λ³€κ²½ν•˜μ§€ μ•ŠμŒ

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

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"]

 

 

ν΄λ‘œμ € ν‘œν˜„ 문법 (Closure Expression Syntax)

ν΄λ‘œμ € ν‘œν˜„ 문법 (일반)

{ (parameters) -> return type in
	statements
}

 

reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
	return s1 > s2
})
  • 인라인 ν΄λ‘œμ €: ν•¨μˆ˜λ‘œ λ”°λ‘œ μ •μ˜λœ ν˜•νƒœκ°€ μ•„λ‹Œ 인자둜 λ“€μ–΄κ°€ μžˆλŠ” ν˜•νƒœμ˜ ν΄λ‘œμ €

λ¬Έλ§₯μ—μ„œ νƒ€μž… μΆ”λ‘  (Inferring Type From Context)

sorted(by:) 의 λ©”μ†Œλ“œμ—μ„œ (String, String) -> Bool νƒ€μž…μ˜ μΈμžκ°€ 듀어와야 ν•˜λŠ”κ±Έ μ•ŒκΈ° λ•Œλ¬Έμ—  ν΄λ‘œμ €μ—μ„œ 이 νƒ€μž…λ“€μ€ μƒλž΅ ν•  수 있음

이처럼 νƒ€μž…μ„ μž‘μ„±ν•˜μ§€ μ•Šμ•„λ„ νƒ€μž…μ„ μΆ”λ‘ ν•΄μ„œ μ •μƒμ μœΌλ‘œ ν΄λ‘œμ €κ°€ μˆ˜ν–‰λ¨

reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
  • ν•˜μ§€λ§Œ 가독성과 μ½”λ“œμ˜ λͺ¨ν˜Έμ„±μ„ ν”Όν•˜κΈ° μœ„ν•΄ νƒ€μž…μ„ λͺ…μ‹œν•˜λŠ”κ±Έ ꢌμž₯

단일 ν‘œν˜„ ν΄λ‘œμ €μ—μ„œμ˜ μ•”μ‹œμ  λ°˜ν™˜ (Implicit Returns from Single-Express Closures)

단일 ν‘œν˜„ ν΄λ‘œμ €μ—μ„œλŠ” λ°˜ν™˜ ν‚€μ›Œλ“œ(return)λ₯Ό μƒλž΅ν•  수 있음

reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )  // retrun μƒλž΅

인자 이름 μΆ•μ•½ (Shorthand Arguments Names)

인라인 ν΄λ‘œμ €μ— μžλ™μœΌλ‘œ μΆ•μ•½ 인자 이름을 제곡

인자 값을 μˆœμ„œλŒ€λ‘œ $0, $1, $2 λ“±μœΌλ‘œ μ‚¬μš©κ°€λŠ₯

μΆ•μ•½ 인자 이름을 μ‚¬μš©ν•˜λ©΄ 인자 κ°’κ³Ό κ·Έ 인자둜 μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©ν•˜λŠ” μΈμžκ°€ κ°™λ‹€λŠ” 것을 μ•ŒκΈ°μ— 인자λ₯Ό μž…λ ₯ λ°›λŠ” λΆ€λΆ„κ³Ό in ν‚€μ›Œλ“œ 뢀뢄을 μƒλž΅ κ°€λŠ₯

reversedNames = names.sorted(by: { $0 > $1 } )

μ—°μ‚°μž λ©”μ†Œλ“œ (Operator Methods)

sorted(by:) λ©”μ†Œλ“œμ—λŠ” 인자 λ‘κ°œλ₯Ό λ°›μ•„ 두 값을 λΉ„κ΅ν•œλ‹€λŠ”κ±Έ (>) μ—°μ‚°μž λ©”μ†Œλ“œλ‘œ μΆ”λ‘ ν•  수 μžˆμœΌλ―€λ‘œ μ•„λž˜μ™€ 같이 좕약도 κ°€λŠ₯

reversedNames = names.sorted(by: >)

 

ν›„ν–‰ ν΄λ‘œμ € (Trailing Closures)

ν•¨μˆ˜μ˜ λ§ˆμ§€λ§‰ 인자둜 ν΄λ‘œμ €λ₯Ό λ„£κ³  κ·Έ ν΄λ‘œμ €κ°€ κΈΈλ‹€λ©΄ ν›„ν–‰ ν΄λ‘œμ €λ₯Ό μ‚¬μš©κ°€λŠ₯

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}
someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}
  • μœ„μ˜ ν•¨μˆ˜μ™€ ν΄λ‘œμ €λ₯Ό λ°‘μ˜ ν›„ν–‰ ν΄λ‘œμ €λ‘œ ν‘œν˜„ κ°€λŠ₯

κ°’ 캑처 (Capturing Values)

μ •μ˜λœ μ£Όλ³€ μ»¨ν…μŠ€νŠΈλ‘œλΆ€ν„° μƒμˆ˜, λ³€μˆ˜μ˜ 값을 μΊ‘μ²˜ν•  수 있음

원본 값이 사라져도 클둜져의 body μ•ˆμ—μ„œ κ·Έ 값을 ν™œμš© κ°€λŠ₯

쀑첩 ν•¨μˆ˜

  • κ°€μž₯ κ°„λ‹¨ν•œ ν΄λ‘œμ €
  • ν•¨μˆ˜ 밖에 μžˆλŠ” μƒμˆ˜, λ³€μˆ˜λ₯Ό μΊ‘μ²˜ν•  수 있음
  • 밖에 μžˆλŠ” ν•¨μˆ˜μ˜ μΈμžλ„ μΊ‘μ²˜ν•  수 있음
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

ν΄λ‘œμ €λŠ” μ°Έμ‘° νƒ€μž… (Closures Are Reference Types)

ν•¨μˆ˜μ™€ ν΄λ‘œμ €λ₯Ό μƒμˆ˜λ‚˜ λ³€μˆ˜μ— ν• λ‹Ήν•  λ•Œ μ‹€μ œλ‘œλŠ” μƒμˆ˜μ™€ λ³€μˆ˜μ— ν•΄λ‹Ή ν•¨μˆ˜λ‚˜ ν΄λ‘œμ €μ˜ μ°Έμ‘°(reference)κ°€ ν• λ‹Ή

let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// 50을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

 

μ΄μŠ€μΌ€μ΄ν•‘ ν΄λ‘œμ € (Escaping Closures)

 ν•¨μˆ˜κ°€λλ‚˜κ³  μ‹€ν–‰λ˜λŠ” ν΄λ‘œμ € μ΄λ©°, 비동기 μž‘μ—…μ„ ν•˜κΈ° μœ„ν•΄ μ‚¬μš©

νŒŒλΌλ―Έν„° νƒ€μž… μ•žμ— @escapingμ΄λΌλŠ” ν‚€μ›Œλ“œλ₯Ό λͺ…μ‹œ

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

β€» @escaping ν΄λ‘œμ €λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” selfλ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ‚¬μš©ν•΄μ•Ό 함

 

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()    // ν•¨μˆ˜ μ•ˆμ—μ„œ λλ‚˜λŠ” ν΄λ‘œμ € = non-escaping
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 } // λͺ…μ‹œμ μœΌλ‘œ selfλ₯Ό μ μ–΄μ€˜μ•Ό ν•©λ‹ˆλ‹€.
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

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

completionHandlers.first?()
print(instance.x)
// Prints "100"

 

μžλ™ν΄λ‘œμ € (Autoclosures)

ν•¨μˆ˜μ˜ 인자둜 μ „λ‹¬λ˜λŠ” μ½”λ“œλ₯Ό κ°μ‹Έμ„œ μžλ™μœΌλ‘œ ν΄λ‘œμ €λ₯Ό λ§Œλ“€μ–΄μ€Œ

ν΄λ‘œμ €λ₯Ό μ‹€ν–‰ν•˜κΈ° μ „κΉŒμ§€ μ‹€μ œ μ‹€ν–‰ν•˜μ§€ μ•ŠμŒ

  • 계산이 λ³΅μž‘ν•  λ•Œ, ν•„μš”ν•  λ•Œλ§Œ 호좜되기 λ•Œλ¬Έμ— 유용
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"

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!"
// 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!"
  • @autoclosureν‚€μ›Œλ“œλ₯Ό μ΄μš©ν•΄μ„œ 보닀 κ°„κ²°ν•˜κ²Œ μ‚¬μš© κ°€λŠ₯
λ°˜μ‘ν˜•