클로져 Closures
클로져 (Closure)는 함축적(self-contained) 기능들의 블럭입니다. 스위프트에서 클로져는 C와 Objective-C의 클로져 또는 다른 프로그래밍 언어의 람다(lambdas)와도 유사합니다.
클로져는 문맥(context)로부터 상수나 변수에 대한 참조들을 가져와서(캡쳐링, capturing) 저장할 수 있습니다. 이것을 변수나 상수들을 클로징 오버(closing over) 한다고 말합니다. 스위프트는 여러분을 대신하여 캡쳐링에 필요한 메모리 관리를 직접 수행합니다.
참고
만약 여러분이 캡쳐링에 대한 개념에 익숙하지 않아도 괜찮습니다. 값 캡쳐링 (Capturing Values)에서 자세한 정보를 확인하세요.
전역 함수와 중첩된 함수는 모두 클로져의 특별한 종류입니다. 다음 세 개의 형태 중 하나에 해당한다면 클로져입니다.
- 어떠한 값도 캡쳐링하지 않지만, 이름은 존재하는 전역 함수는 클로져다.
- 중첩 함수를 감싸는 함수로부터 값을 캡쳐링할 수 있고, 이름이 존재하는 중첩 함수는 클로져다.
- 간단한 문법에 의해 작성되었으며, 주변의 문맥으로부터 값을 캡쳐링할 수 있는 클로져 표현식은 이름이 없는 클로져이다.
스위프트의 클로져 표현식은 보통의 경우에서 간결하고 산만함이 없는 깔끔하고, 명확한 형식을 가집니다.
이러한 형식들은 다음을 포함합니다 :
- 문맥으로부터 파라미터 추론과 값의 타입 반환
- 단일 표현식 클로져로부터 암시적 반환
- 약식 인자 이름
- 클로져 문법 트레일링
클로져 표현식
중첩 함수 (Nested Functions) 에서 소개한 중첩 함수는 함축적 코드 블럭을 명시하는 간편한 방법입니다. 하지만 이름이 없고 완전한 선언식 없이 함수와 비슷한 구조물을 짧게 작성하는 것이 때로는 유용합니다. 특히, 하나 이상의 함수를 인자로 받는 함수를 작성할 때 그렇습니다.
클로져 표현식은 인라인 클로져를 명료하고 군더더기 없는 문법으로 작성하는 방법 중 하나입니다. 클로져 표현식은 여러 문법 최적화를 제공합니다. 예를 들어, 코드의 명료함을 잃지 않고 약식으로 클로져를 작성할 수 있습니다. 아래의 클로져 표현식 예제는 여러 순회를 거치는 sorted(by:)
메서드의 예제입니다. 이 표현식을 이용하여 간결한 방법으로 같은 기능을 표현해 볼 것입니다.
정렬 메서드
스위프트의 스탠다드 라이브러리는 sorted(by:)
라는 메서드를 제공합니다. 이 메서드는 이후에 여러분이 작성할 정렬 클로져의 출력을 기반으로 특정 타입의 값들의 배열을 정렬합니다. 정렬 클로져가 정렬을 완료하면 sorted(by:)
메서드는 같은 타입과 같은 크기의 새로운 배열 올바르게 정렬하여 반환합니다. sorted(by:)
메서드는 정렬 대상인 원본은 수정하지 않습니다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Dniella"]
sorted(by:)
메서드는 클로져를 받습니다. 이 클로져는 같은 타입의 두 인자를 받습니다. 그리고 Bool
값을 반환하여 첫 번째 값이 두 번째 값보다 먼저 나와야하는지 아닌지를 알려줍니다. 정렬 클로져는 첫 번째 값이 두 번째 값보다 앞에 위치해야 한다면 true
를 반환하고 그 반대라면 false
를 반환합니다.
String
값의 배열을 정렬하는 예제를 살펴보겠습니다. 정렬 클로져는 (String, String) -> Bool
타입의 함수입니다.
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames 는 ["Ewa", "Daniella", "Chris", "Barry", "Alex"] 이다.
첫 문자열 (s1
)이 두 번째 문자열 (s2
)보다 크다면, backward(_:_:)
함수는 true
를 반환합니다. s1
이 s2
보다 배열의 앞에 존재해야 한다는 것을 말해줍니다. 문자열의 문자를 보면, "greater than"은 "알파벳의 나중에 등장한다"는 것을 의미합니다. 즉, "B"
는 "A"
ㅂㅗ다 크다는 것입니다. 따라서, "Tom"
은 "Tom"
보다 작습니다.
하지만 이것은 단순히 (a > b
)를 표현하기에 너무 깁니다. 클로져 표현식 문법을 사용하여 정렬 클로져를 인라인으로 작성하는 것이 선호되는 이유입니다.
클로져 표현식 문법
클로져 표현 문법은 다음의 일반적인 형태를 따릅니다.
{ (파라미터) -> 리턴 타입 in
실행할 문장
}
여기서 사용된 _parameters_는 클로져 표현식 문법에 작성되어 인-아웃 파라미터가 될 수 있습니다. 하지만 기본 값은 가지지 못합니다. 가변인자 파라미터를 사용한다면 가변인자를 사용할 수 있습니다. 튜플 또한 리턴 타입과 파라미터 타입이 될 수 있습니다.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2})
여기서 주목해야할 점은 인라인 클로져로 사용된 파라미터와 리턴 타입의 선언이 backward(_:_:)
함수의 선언과 동일하다는 것입니다. 두 가지 방법에서, 공통적으로 (s1: String, s2: String) -> Bool
로 작성이 되었습니다. 하지만, 인라인 클로져 표현식은 파라미터와 리턴타입이 중괄호 ( { }
) 내부에 작성되어 있다는 것입니다.
클로져 내부의 시작은 in
키워드로 시작합니다. 이 키워드는 클로져의 파라미터와 리턴타입의 명시가 종료되었음을 의미하며, 클로져의 내부가 곧 시작됨을 말해줍니다.
클로져의 내부는 매우 짧으므로, 한 줄에 작성될 수 있습니다.
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2})
sorted(by:)
함수를 호출하는 것은 여전히 같습니다. 소괄호 쌍은 여전히 전체 인자를 감쌉니다. 하지만 인자는 이제 인라인 클로져가 되었습니다.
문맥으로부터 타입 추론하기
정렬 클로져는 인자로서 메서드에 전달되기 때문에, 스위프트는 해당 메서드의 파라미터의 타입과 그 메서드가 반환해야할 값의 타입을 자동으로 추론합니다. sorted(by:)
메서드는 문자열의 배열에 의해 호출되며 이 함수의 인자는 (String, String) -> Bool
타입의 함수가 되어야 합니다. 즉, 클로져 표현식을 정의할 때 (String, String)
과 Bool
타입은 작성될 필요가 없다는 것입니다. 모든 타입은 추론되므로 반환 타입인 (->
) 과 파라미터의 이름들 주변을 감싸는 소괄호가 생략될 수 있습니다.
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 })
인라인 클로져 표현식으로서 클로져에 함수나 메서드를 전달할 때, 파라미터 타입과 반환 타입을 추론하는 것은 항상 가능합니다. 따라서, 여러분은 클로져가 함수나 메서드의 인자로 사용될 때, 인라인 클로져를 완전한 형식으로 작성할 필요가 없습니다.
하지만, 여러분은 원한다면 타입을 명시적으로 작성할 수 있습니다. 게다가 코드를 읽는 사람에게 모호함을 주지 않도록 하고 싶다면 권장하기도 합니다. sorted(by:)
메서드의 경우, 클로져의 목적이 명확하므로 인라인 클로져 표현식을 사용해도 무방합니다.
단일 표현식의 클로져로부터 암시적 반환
단일 표현식 클로져는 암시적으로 단일 표현식의 결과를 반환할 수 있습니다. 이 방법은 return
키워드를 생략하여 가능합니다. 이전 예제를 활용하면 다음과 같습니다.
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 })
여기서, sorted(by:)
메서드 인자의 함수 타입은 Bool
값이 클로져에 의해 반환되어야 함을 명확하게 말합니다. 클로져 내부는 단일한 표현식인 (s1 > s2)
을 포함하고 있으므로, 반환 타입에 대한 모호함이 없습니다. 따라서 return
키워드 생략이 가능합니다.
약식 인자 이름
스위프트는 자동으로 인라인 클로져에 약식 인자 이름을 제공합니다. 이것은 $0
,$1
, $2
과 같이 클로져 인자의 이름을 작성하여 값을 추론하는데 사용됩니다.
만약 약식 인자 이름을 클로져 표현식 내부에 사용하면 클로져의 인자 목록들을 생략할 수 있으며, 약식 인자 이름의 타입의 개수가 기대한 함수의 타입으로부터 추론됩니다. in
키워드 또한 생략될 수 있습니다. 클로져 표현식이 전체적으로 자신의 내부에서 만들어지기 때문입니다.
reversedNames = names.sorted(by: { $0 > $1 })
여기서 $0
와 $1
은 클로져의 첫 번째와 두 번째 String
인자를 추론합니다.
연산자 메서드
심지어는 더 짧게 클로져 표현식을 작성하는 방법이 있습니다. 스위프트의 String
타입은 문자열에 한정된 >
연산자를 정의하고 있습니다. 이 연산자는 타입이 String
인 두 개의 파라미터를 가지며 Bool
타입의 값을 반환합니다. sorted(by:)
메서드가 필요한 메서드 타입과 이 연산자의 타입은 정확히 일치합니다. 그러므로, >
연산자만을 인자로 전달하여, 여러분이 문자열에 한정된 구현 코드를 사용하려는 것을 스위프트가 추론할 것입니다.
reversedNames = names.sorted(by: >)
연산자 메서드에 대한 자세한 사항은 연산자 메서드(Operator Methods) 를 참고하세요.
출처 : https://docs.swift.org/swift-book/LanguageGuide/Closures.html
'Swift > Swift Language' 카테고리의 다른 글
Swift 언어 가이드 - 트레일링 클로져 (0) | 2020.03.05 |
---|---|
Swift 언어 가이드 - 클로져 기본 (0) | 2020.03.05 |
Swift 언어 가이드 - 함수 (0) | 2020.02.18 |
Swift 언어 가이드 - 문자열과 문자 (0) | 2020.02.17 |
Swift 언어 가이드 - 흐름 제어 (0) | 2020.02.16 |