클로저(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 |
댓글