클로져
- 코드내에서 전달되고 사용되는 기능 단위가 될 수 있다?
- C와 Object-C에서 블록과 유사하고 다른 언어의 람다와 비슷하다.
- 클로져가 정의된 콘텍스트 내에서 상수나 변수에 대한 참조를 캡쳐하고 저장할 수도 있다. 이런 상수와 변수를 클로징이라 부르며 이래서 클로저라고 부른다. Swift는 클로저에 필요한 메모리 관리를 책임진다.
- 주의 :
capturing개념에 익숙하지 않더라도 걱정하지마라. capturing 값에서 자세히 알아보자. - 전역 그리고 네스티드 함수는 실제로 클로저의 특수한 형태라 할 수 있다. 클러저의 형태 :
- 전역 함수 : 이름은 있지만 어떤 값도 캡쳐하지 않는다.
- 네스티드 함수 : 이름을 가지고 범위내에 값을 캡쳐할 수 있다.
- 클로져 표현 : 단순한 문법으로 이름을 가지지 않는 클로저이며 둘러싸고 있는 컨텍스트에서 값을 캡쳐할 수 있다.
- Swift의 클로저 표현은 깔끔하고 명확한 스타일로 일반 시나리오에서 간결한 문법으로 최적화된다. 아래와 같은 최적화를 포함하고 있다 :
- 컨텍스트내에서 파라미터와 반환값의 타입을 예상
- 단일 표현 클로저에서 묵시적으로 반환
- 단축화 시킨 인자 이름
- 클로저 문법을 trailing
클로저 표현
- 네스티드 함수는 코드의 자기 포함 블록을 정의하고 이름을 붙이는데 편리한 수단이다. 하지만 가끔은 전체 선언이나 이름 없이 함수 같은 생성자의 단축 버전을 작성하는데 유용하다. 이는 특히 1개 이상의 인자로 함수를 가지는 함수나 메소드를 처리하는데 유용하다.
- 클로저 표현은 간단히 인라인 클로저를 작성하는 방식이며 문법이다. 클로저 표현은 의도나 명료함을 잃지 않고도 단축된 형태로 클로저를 사용하고 최적화된 여러 문법을 제공하고 있다. 클로저 표현 예제는 sort 메소드의 예제를 정의하면서 여러 최적화를 보도록 하자. 각 표현은 동일한 기능을 수행하면서 점점 간단 명료하게 된다.
Sort 메소드
- Swift의 표준 라이브러리에서 sort라는 메소드를 제공하고 있다. 이 메소드는 알려진 타입의 값의 배열을 정렬한다. 여러분이 제공할 정렬 클로저의 결과를 기반으로 하고 있다. 일단 정렬 프로세스를 완료하면 sort 메소드는 제대로 정렬된 순서의 엘리먼트를 가진 동일 타입의 새로운 배열을 반환하고 크기는 기존 값과 같다. 원본 배열은 sort 메소드에 의해 변경되지는 않는다.
- 클로저 표현은 알파벳 역순으로 String 값의 배열을 정렬하는데 sort 메소드를 사용한다. 여기 정렬할 초기 배열은 아래와 같다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
- sort 메소든느 배열의 컨텐츠로 동일 타입의 2개 인자를 가지는 클로저를 받는다. Bool 값을 반환하는데 값을 정렬하고 첫번째 값이 두번째 값 전이나 후에 나타나는지 여부를 알려주는 역할을 한다. 정렬 클로저는 첫번째 값이 두번재 값 전에 나타나면 true를 반환하고 그렇지 않은 경우 false를 반환한다.
- 이 예제에서 String 값의 배열을 정렬하고 정렬 클로저는
(String, String) -> Bool타입의 함수가 되야한다. - 정렬 클로저를 제공하는 방법 중에 하나는 올바른 타입의 일반 함수를 작성하고 인자로 sort 메소드를 넘기는 것이다.
func backwards(s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversed = names.Sort(backwards)
//역순으로 정렬하면 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
- 첫번째 s1은 2번째 s2보다 크면 backwards 함수가 true를 반환할 것이다. 이것은 정렬된 배열내에서 s1이 s2전에 위치하고 있다는 것을 뜻한다. 문자열에 있는 문자에서
~보다 크다는 것은알파벳에서 ~보다 뒤에 있다라는 뜻이다. 이 말은 문자B는 문자A보다 크다는 것이다. 문자열Tom은Tim보다 크다. 역순으로 정렬된 경우 "Barry"가 "Alex" 이전에 나타난다. - 하지만 근본적으로 단일 표현 함수
(a>b)를 작성하는데 길고장황하다. 이 예제에서 클로저 표현 문법을 이용해서 정렬 클로저 인라인을 작성하는 것을 선호한다.
클로저 표현 문법
- 클로저 표현 문법은 다음과 같은 형태를 갖는다.
{ (parameters) -> return type in
statements
}
- 클로저 표현 문법은 상수 파라미터, 변수 파라미터, inout 파라미터를 사용할 수 있다. 기본 값은 제공되지 않는다. 가변길이 파라미터는 가변길이 파라미터에 이름을 붙여주면 가변길이 파라미터로 사용할 수 있다. 튜플은 파라미터 타입으로도 사용할 수 있고 반환 타입으로도 가능하다.
아래 예제는 backwards 함수의 클로저 표현 버전이다.
reversed = names.sort({(s1: String, s2: String) -> Bool in return s1 > s2인라인 클로저에 대한 파라미터의 선언과 반환 타입은 backwards 함수로부터 선언한 것과 동일하다. 이 두가지 케이스에서 (s1: String, s2: String) -> Bool처럼 작성한다. 그러나 인라인 클로저 표현에 대해 파라미터와 반환 타입은
{}내부에만 작성한다.- 클러저 몸통부분의 시작은 in 키워드로 시작한다. 이 키워드는 클로저의 파라미터의 정의를 표시하고 반환 타입을 마치게 되고 클로제 몸통이 시작된다.
- 클로저 몸통이 짧기 때문에, 한 줄로 작성할 수 있다.
reversed = names.sort({(s1: String, s2: String) -> Bool in return s1 > s2})
- 이것은 sort 메소드에 대해서 전체 호출은 동일하다는 것을 보여준다. 괄호쌍은 여전히 메소드에 대해서 전체 인자를 감싸고 있다. 그러나 이 인자는 이제 인라인 클로저가 된다.
컨텍스트로부터 타입 추측하기
- 정렬 클로저는 인자로 메소드에 전달되며, Swift는 파리미터의 타입과 반환할 값의 타입을 예측한다. sort 메소드는 문자열의 배열로 호출된다. 따라서 이것의 인자는 반드시 (String, String) -> Bool 타입의 함수여야만 한다. 이말은 (String, String)와 Bool 타입은 클로저 표현 정의로 작성할 필요가 없다는 뜻이다. 모든 타입을 에측할 수 있기 때문에 반환 화살표 -> 와 파라미터를 감싸는 괄호는 생략할 수 있다.
reversed = names.sort({ s1, s2 in return s1 > s2 })
- 클로저를 함수나 메소드에 인라인 클로저 표현으로 전달할때, 파라미터 타입과 반환 타입을 항상 예측할 수 있다.
- 결과적으로 인라인 클로저를 작성할 때 문법을 모두 갖춰서 작성할 필요가 없다.
- 그럼에도 불구하고 여러분이 원한다면 타입을 명시적으로 표현할 수도 있다. 그렇게 한다면 여러분의 코드를 읽는 사람들이 모호하게 느끼는 것을 회피할 수 있다. sort 메소드의 경우에 클로저의 목적은 정렬이 발생하고 있다는 사실에서 명확하다. 그리고 읽는 사람이 클로저는 String 값과 동작할 수 있따는 것을 가정하게 된다. 왜냐하면 문자열의 배열의 정렬을 돕고 있기 때문이다.
단일 표현 클로저에서 암묵적 반환
- 단일 표현 클로저는 선언에서 return 키워드를 생략함으로써 단일 표현의 결과를 암묵적으로 반환할 수 있다. 이전 예제를 사용해서 표현하면
reversed = names.sort( { s1, s2 in s1 > s2 })
- 여기 sort 메소드의 인자의 함수 타입은 Bool 값은 반드시 클로저로 반환해야만 한다는 것을 명확히 나타낸다. 만약 클로저의 몸체가 Bool 값을 반환하는 단일 표현 (s1 > s2)를 포함한다. 여기에는 모호함이 없고 return 키워드는 생략가능하다.
단축 인자 이름
- Swift는 인라인 클로저에 자동으로 단축 인자 이름을 제공한다. 이는 $0, $1, $2라는 이름으로 클로저의 인자 값에 대해서 참조할 수 있다.
- 여러분의 클로저 표현내부에서 단축 인자 이름을 사용한다면, 클로저 정의에서 인자 리스트를 생략할 수 있다. 그리고 단축 인자 이름의 숫자와 타입은 기대한 함수 타입으로부터 예측할 수 있다. in 키워드는 생략할 수 있다. 왜냐하면 클로저 표현은 온전히 몸체만으로 구성된다.
reversed = names.sort( { $0 > $1 } )
- 여기서 $0와 $1는 클로저의 첫번째와 두번째 String 인자를 참조한다.
연산자 함수
- 실제로 위에 클로저 표현을 작성하는 것보다 짧게도 가능하다. Swfit의 String 타입은 ~보드 크다는 기호인
>의 문자열 지정 구현을 정의한다. 이는 정확히 sort 메소드에서 필요로 하는 함수 타입과 일치한다. 따라서 여러분은 단순히 > 연산자에 전달할 수 있고 Swift는 문자열에 특화된 구현을 사용하길 원한다고 추측할 것이다.
reversed = names.sort(>)
Trailing 클로저
- 만약 함수의 마지막 인자와 클로저 표현이 길어서 클로저 표현을 함수에 전달하고 싶으면, 대신에 trailing 클로저를 작성하면 유용하다. trailing 클로저는 함수 호출의 괄호 바깥에 작성한 클로저 표현이다.
func someFunctionThatTakesAClosure(closure: () -> Void) {
}
someFunctionThatTakesAClosure( {
})
someFunctionThatTakesAClosure() {
}
- 클로저 표현 문법 섹션에서 문자열 정렬 클로저는 trailing 클로저로 sort 메소드의 괄호 바깥에 작성할 수 있다.
reversed = names.sort() { $0 > $1 }
- 클로저 표현이 함수나 메소드의 유일한 인자로 제공되고 여러분은 trailing 클로저로 표현을 제공한다면, 함수를 호출할 때 함수나 메소드 이름 이후에 괄호쌍을 쓸 필요가 없다.
reversed = names.sort { $0 > $1 }
- trailing 클로저는 클로저가 한 줄 인라인에 쓰기에 불가능할 정도로 길때 유용하다. 예제로 Swift의 Array 타입은 map 메소드를 가진다. 이 메소드는 단일 인자로 클로저 표현을 가진다. 클로저는 배열에 각 아이템에 대해서 한 번 호출되고 해당 아이템에 대해서 선택적으로 map 값을 반환한다. 매핑의 속성과 반환되는 값의 타입은 구체적으로 명시하기 위해 해당 클로저에 남긴다.
- 제공한 클로저를 각 배열 엘리먼트에 적용한 후에, map 메소드는 새로 매핑된 값 모두를 포함하는 새로운 배열을 반환한다. 원래 배열에 있는 값과 동일한 순서를 따른다.
- Int 값의 배열을 String 값의 배열로 변환하기 위해서 trailing 클로저로 map 메소드를 사용하는 방법이 여기 있다. 배열 [16, 58, 510]은 새로운 배열 ["oneSix", "FiveEight", "FiveOneZero"]를 생성한다. ```swift let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ]
let numbers = [16, 58, 510] ```
- 위 코드는 정수 숫자와 영어로
정의
사용용도
문법
예제
Quiz
- 정의에 대한 질문
- 용도에 대한 질문
- 예제 관련