본문 바로가기
⌨️ 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키워드를 이용해서 보다 간결하게 사용 가능
반응형

'⌨️ Language > swift' 카테고리의 다른 글

[Swift] 프로퍼티  (0) 2024.03.20
[Swift] 클래스와 구조체  (0) 2024.03.19
[Swift] 예외 처리와 함수  (4) 2024.03.17
[Swift] 조건/반복문  (1) 2024.03.17
[Swift] 콜렉션 타입  (0) 2024.03.16

댓글