스위프트의 중요한 기능중 하나인 closure에 대해 알아봅시다.
코드들이 모인 그룹을 closure라고 설명하고 있습니다.
closure 에는 크게 Named closure, Unnamed closure로 나누어져 있으며 우리가 보통 말하는 closure은 Unnamed closure를 지칭합니다.
Named closure : 보통 func를 이용한 함수를 named closure라고 합니다.
이 unnamed closure은 다른 언어의 람다함수와 비슷한 형식을 취하고 있습니다.
closure 또한 이름만 없는 함수 이기 때문에 1급 객체의 특성을 따라 함수의 파라미터로 사용이 가능하며 반환값으로도 사용이 가능합니다.
기본적인 표현식은
{ (Parameters) -> ReturnType in
code
...
}
로 작성이 가능하며 in 앞에 구문들을 head, in 뒤에 구문들을 body라고 합니다.
head와 body를 in 으로 나누어 구분합니다.
앞에서 말했듯이 스위프트에서 함수는 1급 객체로써 파라미터로 사용이 가능합니다.
그래서 함수의 파라미터로 전달할 때에 사용이 가능합니다.
가이드북에 나와있는 예제를 보면서 알아봅시다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
이름을 모아놓은 배열이 있고 이것을 역알파벳 순으로 정렬을 하려고 합니다.
스위프트에서는 정렬을 해주는 메소드, sorted(by:) 가 있어 이것을 활용하려 합니다.
이 메소드는
func sorted(by areInIncreasingOrder: (Self.Element, Self.Element) throws -> Bool) rethrows -> [Self.Element]
두개의 element를 인자로 받고 Bool을 반환하는 함수 를 인자로 하는 함수 입니다.
쉽게 말해 sorted함수의 인자로 정렬하는 방법을 알려주는 함수를 넣어야 하는 것입니다.
가장 쉽게 하려고 하면 함수를 만들어 넣으면 됩니다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reverseName = names.sorted(by: backward)
// reverseName = ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
하지만 클로저를 이용하면 더 깔끔하게 작성이 가능합니다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var reverseNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
하지만 일반적으로 공부하면서 본 클로저들은 저렇게 거추장스럽지 않았던 걸로 기억합니다.
무엇이든지 쓰기 편하게 이해할 수 있는 선에서 최대한 경량화하는게 좋지 않겠습니까??
단어의 뜻과 비슷하게 함수에 뒤에 꼬리를 붙이듯이 클로져를 함수 뒤에 붙이는 형식입니다.
자세하게 말하면
함수의 마지막 매개변수가 클로저 일때 이를 매개변수를 넣는 형식이 아닌 뒤에 클로저를 붙여 작성하는 방법입니다.
제가 봐도 뭔 소리인지 모르겠으니 예를 봅시다.
앞에서 본 sorted 메소드를 보면 파라미터가 하나 있고 클로저입니다.
그러므로 트레일링 클로저를 이용해 경량화가 가능합니다.
// 기존 표현
var reverseNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
// Trailing Closure를 사용
var reverseNames = names.sorted(){ (s1: String, s2: String) -> Bool in
return s1 > s2
}
여기서 파라미터가 비어 괄호 안이 비어있으면 () 또한 생략이 가능합니다.
그러면
var reverseNames = names.sorted{ (s1: String, s2: String) -> Bool in
return s1 > s2
}
이렇게 깔끔하게 정리가 가능해집니다!
그런데 가만보니 클로저 외부만 정리를 하고 클로저 안은 그대로이지 않습니까??
클로저 안 부분도 경량화를 해봅시다.
클로저 안에 head 부분에 있는 파라미터 타입과 반환 타입을 생략할 수 있습니다.
// 생략 전
var reverseNames = names.sorted{ (s1: String, s2: String) -> Bool in
return s1 > s2
}
// 생략 후
var reverseNames = names.sorted { (s1, s2) in
return s1 > s2
}
바로 이렇게 말이죠~
생략이 가능한 이유는 스위프트가 파라미터에 들어갈 타입을 알아서 추론해주기 때문입니다.
반환값 타입 또한 정해져 있기 때문에 당연히 추론이 가능합니다.
여기서 또 클로저 안에 있는 파라미터 또한 생략이 가능합니다.
스위프트에서는 shorthand argument names 를 제공하는데 이것이 클로저 안에 있는 파라미터와 대체가 가능합니다.
$0, $1, $2, …
형식으로 제공이 됩니다.
쉽게 예를 보며 이해해볼까요?
var reverseNames = names.sorted { (s1, s2) in
return s1 > s2
}
// 여기서 차례대로 s1 = $0, s2 = $1로 대체가 가능합니다.
// 대체함과 동시에 클로저 안 파라미터와 in이 생략이 가능합니다.
var reverseNames = names.sorted {
return $0 > $1
}
파라미터 갯수에 따라 앞에서부터 차례대로 $0, $1, $2, … 로 부여됩니다.
이렇게 shorthand argument names를 사용하면 클로저 안 파라미터와 in 키워드를 생략할 수 있고
결과적으로 더욱 깔끔한 코드가 완성되게 됩니다.
여기서 마지막으로 클로저 안에 다른 구문은 없고 return 구문 한 줄만 남아있을 경우 return 또한 생략이 가능합니다.
// return 생략 가능, 최종적으로 경량화가 완료된 구문
var reverseNames = names.sorted {
$0 > $1
}
// 기존 생략 전
var reverseNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
길었던 기존 클로저를 한 줄로 깔끔하게 표현할 수 있습니다.
이렇게 간편한 클로저를 사용하여 다들 날먹코딩 합시다. ^0^