클로저는 C와 Objective-C의 블록 혹은 다른 언어의 람다와 유사합니다. 문맥에 정의된 상수와 변수의 값을 캡처해서 저장할 수 있고 전역 함수, 중첩 함수, 익명 함수 등 여러가지 형태를 취합니다. 일반적으로 클로저는 익명 함수의 형태로 많이 쓰이며, 이를 클로저 표현식(Closure Expressions)라고도 합니다.

클로저 표현식

중첩 함수는 함수 내의 독립적인 코드 블록을 정의해서 이름을 붙이는 편리한 수단입니다. 하지만 중첩 함수보다 더 짧은 구조의 클로저 표현식을 사용하는 것이 더욱 유용할 때가 있습니다. 클로저 표현식은 인라인 클로저를 명확하게 표현하는 방법으로 초점이 맞춰져 있습니다.

Swift 표준 라이브러리에서 제공하는 sorted(by:)를 통해 어떻게 최적화를 할 수 있는지 알아봅시다. sorted(by:)는 배열의 요소를 정렬하는데 사용되며, 어떻게 정렬할지 by 인자에 함수 혹은 인라인 클로저로 제공할 수 있습니다. 아래의 names 배열을 알파벳 순으로 정렬할 때 함수와 인라인 클로저를 사용하는 방식을 비교해 봅시다.

let names = [“Chris”, “Alex”, “Ewa”, “Barry”, “Daniella”]

우선 함수를 제공하는 방식입니다. String 타입의 두 인자를 비교하는 backward 함수를 작성해서 sorted(by:)의 인자에 제공합니다.

func backward(_ s1: String, _ s2: String) -> Bool {
  return s1 < s2
}

let sortedNames = names.sorted(by: backward)

그리고 인라인 클로저를 제공하는 방식입니다.

sortedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
  return s1 < s2
})

두 방법은 각각의 장단점이 있지만 정답은 없습니다. 만약 동일한 정렬을 여러 곳에서 사용한다면 함수가 적절할 수 있지만, 어떤 조건으로 정렬을 하는지 알기 위해서 함수의 구현을 살펴봐야 합니다. 또한 인라인 클로저는 어떻게 정렬하는지 알아보기 편하지만, 동일한 정렬을 여러 곳에서 사용한다면 중복된 코드가 발생합니다. 상황에 맞는 적절한 방식을 채택하는게 좋습니다.

클로저를 통한 최적화

클로저는 함수의 의도를 잃지 않고 간결한 형태로 최적화할 수 있습니다.

1. 문맥에서 타입 추론

sortedNames = names.sorted(by: { s1, s2 -> Bool in return s1 > s2 })

인라인 클로저에서 s1, s2 인자의 타입을 명시하지 않았습니다. 하지만 sorted(by:)는 정렬하고자 하는 names 배열이 [String] 타입인 것을 확인하고 인자의 타입을 추론합니다. 즉 인자의 타입을 생략할 수 있습니다.

2. 단일 표현 클로저에서의 암시적 반환

sortedNames = names.sorted(by: { s1, s2 in s1 > s2 })

단일 표현식이란 s1 > s2 을 의미하며, 단일 표현식 클로저는 선언에서 return 키워드를 생략하고 암시적으로 반환할 수 있습니다. 클로저의 본문이 Bool 값을 반환하는 단일 표현식을 가진 단일 표현 클로저이므로 생략할 수 있습니다.

3. 축약된 인자 이름