[Swift5] Closures 1

Junyoung Park·2022년 2월 25일
0

Swift5 Docs

목록 보기
14/37
post-thumbnail
  • 다음은 Swift 5.6 Doc의 Closures 공부 내용을 정리했음을 밝힙니다.

Closures

클로저는 코드 내에서 사용하고 전달될 수 있는 블록 단위로 C나 Objective-C의 블록이나 다른 프로그래밍 언어의 람다와 유사하다.

클로저는 선언된 문맥에서 상수나 변수를 참조한 값을 캡처하거나 저장할 수 있다. 이를 변수나 상수에 대한 클로징(closing over those constants and variables)이라고 한다. 스위프트는 이를 위한 메모리 관리를 지원하고 있다.

클로저는 세 가지 형태가 있다.

  • 전역 함수: 이름이 있고 값을 캡처하지 않는 클로저
  • 중첩 함수: 이름이 있고 자기를 감싼 함수의 값을 캡처할 수 있는 클로저
  • 클로저 표현: 자기를 둘러싼 문맥에서 값을 캡처할 수 있는 이름이 없는 클로저

스위프트는 클로징을 통해 간결하고 최적화에 적합한 코드를 지원하고 있다. 컨텍스트 내에서 파라미터와 리턴 값을 추론하고, 싱글 익스프레션 클로저에서 리턴을 생략하거나, 아규먼트 이름을 단축어로 쓰거나, 트레일링 클로저 문법 등이 이 최적화 사항이다.

클로저 표현

중첩 함수는 더 큰 함수의 일부를 코드 블록으로 표현하기 위한 방법이다. 하지만 이름이나 전체 선언 없이 함수와 유사한 구조로 짧게 코드를 작성하는 게 유요할 때가 있다. 특히 아규먼트의 일부로 사용할 함수 또는 메소드를 쓸 때 말이다.

클로저 표현(closure expressions)로 짧게 인라인 클로저를 사용할 수 있다. 더 간결한 방식으로 최적화에 적합한 클로저를 사용해보자.

정렬 메소드

sorted(by:) 메소드는 입력값으로 받은 데이터 배열을 정렬한다. 정렬을 마친 뒤 이 메소드는 입력값으로 받은 데이터와 같은 길이, 같은 타입의 정렬된 새로운 배열을 반환한다. 원본 데이터 자체가 정렬되지는 않았다는 데 주목하자.

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

sorted(by:) 메소드는 배열 원소로서 같은 타입의 아규먼트 두 개를 받는 클로저를 받아들인 뒤, 값이 정렬되고 나면 첫 번째 데이터 값이 두 번째 데이터 값 앞 또는 뒤에 나타나야 하는지를 알려주는 불리언 값을 리턴한다. 첫 번째 값이 두 번째 값 앞에 나타나야 하면 true, 그렇지 않으면 false를 리턴한다.

위 예시의 경우 문자열 배열을 정렬하기 때문에 클로저는 (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"]

sorted 메소드에서 사용한 backward는 주어진 배열의 각 값을 정렬하는 기준이 된다. ab보다 작기 때문에 주어진 문자열은 알파벳 반대 순서대로 정렬된다. 하지만 이 경우 backward 함수를 외부에 길게 써야 하는데, 보다 짧게 쓸 수 있다.

클로저 표현 문법

클로저 표현을 쓸 때 이 문법을 따른다. {(parameters) -> return type in statements}

이때 파라미터는 인-아웃일 수는 있지만, 디폴트 값을 설정할 수는 없다. 가변 파라미터도 이름을 선언한 뒤에는 사용할 수 있다. 파라미터 타입이나 리턴 타입으로 튜플 역시 쓸 수 있다.

위의 정렬 메소드에 사용한 backward 함수를 이렇게 써보자.

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

인라인 클로저 표현에서 파라미터와 리턴 타입을 () 안에 적어야 한다는 데 주의하자.

클로저 안의 내용이 짧다면 더 짧게 쓸 수도 있다.

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

이 경우 아규먼트는 인라인 클로저다.

문맥에서 타입 추론

정렬 클로저가 아규먼트로서 메소드에 전달되는데, 이때 스위프트는 파라미터와 리턴할 값 타입을 추론할 수 있다. sorted(by:) 메소드가 문자열 배열을 받기 때문에 아규먼트는 (String, String) -> Bool임을 알 수 있다. 추론 가능하기 때문에 클로저 표현을 작성할 때 쓰지 않아도 된다. 모든 타입이 추론 가능하다면 리턴 화살표나 괄호 또한 생략할 수 있다.

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

이처럼 클로저를 함수나 메소드에 인라인 클로저 표현으로 전달할 때 파라미터나 리턴 타입을 추론할 수 있다. 즉 클로저를 함수나 메소드 아큐먼트로 사용할 때에는 인라인 클로저를 짧게 작성할 수 있다.

가독성을 위해서라면 언제든지 타입을 명확하게 작성할 수도 있다. sorted(by:) 메소드를 쓸 때 클로저를 왜 사용하는지 밝힌다면 문자열 값을 받는다는 것을 보여줄 수 있기 때문이다.

싱글 익스프레션 클로저에서 리턴 생략

한 줄 짜리 클로저(single-expression closures)를 쓸 때 리턴이라는 키워트를 생략할 수 있다.

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

s1 > s2이라는 표현이 불리언 값을 리턴하기 때문에 리턴 타입을 생략할 수 있다.

아규먼트 이름 축약

클로저 표현 안에서 아큐먼트 이름을 축약해서 사용하고 싶다면 클로저의 아규먼트 리스트를 생략할 수 있다. 아규먼트 이름 축약어의 타입은 함수 타입을 예상해서 추론할 수 있다.

$를 통해 클로저가 참조하는 아규먼트를 표시하자. 클로저 표현이 바디 안에서 모두 만들어지기 때문에 in이라는 키워드도 생략할 수 있다.

reversedNames = names.sorted(by: { $0 > $1 } )

$0, $1이라는 표현이 문자열 첫 번째, 두 번째 아규먼트임을 알 수 있고, 이 경우 클로저가 아규먼트 두 개를 사용하고, sorted(by:) 함수가 문자열을 모두 아규먼트로 가지는 클로저를 가지기 때문에 이 축약어 $0, $1 또한 문자열임을 추론할 수 있다.

연산자 메소드

훨씬 더 간략한 표현 방식이 존재한다. 스위프트의 문자열 타입이 지원하는 > 연산자를 통해 문자열을 비교할 수 있기 때문이다. 즉 문자열 파라미터 두 개를 입력받고 불 타입을 리턴할 수 있다. 정확히 앞에서 정렬 메소드에서 사용한 기능과 동이라다.

reversedNames = names.sorted(by: >)
profile
JUST DO IT

0개의 댓글