Closure
코드 내에서 사용되고, 전달될 수 있는 어떠한 기능을 하는 블록. 타 언어의 람다와 비슷하다.
클로저는 정의된 문맥내에서 변수, 상수에 대한 레퍼런스를 캡처하고 저장할 수 있다. 이는 해당 상수, 변수를 Closing 하는것으로 알려져 있으며, Swift는 이러한 캡처의 모든 메모리 관리를 처리한다.
클로저는 다음과같은 세가지 형식을 취한다.
1. 전역함수 (Global Functions) : 이름이 있고, 어떠한 값들도 capture하지 않는다.
2. 내장 함수 (Nested Functions) : 이름이 있고 해당 내장함수를 둘러싸는 함수에 있는 값을 capture한다.
3. 주변 컨텍스트에서 값을 캡쳐할 수 있는 이름이 없는 클로저.
클로저 표현식은 명확성이나, 의도의 손실 없이 함수를 간결하게 작성할 수 있도록 도와준다. 클로저의 최적화는 다음과 같은 항목들을 포함한다.
- 컨텍스트에서 매개변수 및 반환값 유형 추론
- 단일 표현식 클로저의 암시적 반환
- 후행 클로저
- 약식 인수 이름들
클로저 표현식
클로저 표현식은 간결하고 진중된 구문으로 inline 클로저 (매개변수와 반환값이 중괄호 내부에 작성되는 클로저)를 작성하는 방법이다. 이러한 표현식은 명확하고, 의도의 손실 없이 짧은 형식으로 구문을 작성할 수 있게 해준다.
클로저 표현식은 일반적으로 다음과 같은 형식을 가진다.
클로저 표현식 내의 파라미터는 in-out 파라미터가 될 수 있지만, 디폴트 값을 가질 수 없다.
파라미터의 이름을 지정하면 사용할 수 있다, 또한 튜플도 파라미터 또는 리턴값으로 사용될 수 있다.
클로저의 본문은 in 키워드로 시작된다.
인라인 클로저의 예시이다.
sorted(by:) 는 String 타입의 인수 2개를 파라미터로 받고, Bool 타입의 반환값을 반환하는 인라인 클로저를 인수로 받는 함수이다.
여기서 sorted 함수 자체의 반환값은 String 배열이 된다.
문맥에서 유형 추론
해당 함수에 클로저가 파라미터로 전달될 때 Swift는 파라미터의 타입과 반환값의 타입을 유추할 수 있다.
sorted 함수에서 알 수 있듯이, 위 예제에서 sorted 함수는 String 배열에서 호출되므로 해당 함수의 파라미터는 (String, String) -> Bool 여야만 한다. 이는 (String, String)과 Bool 타입이 클로저 표현식 정의의 일부로 작성될 필요가 없음을 의미한다.
즉 해당 문맥에서 타입을 유추할 수 있기 때문에 파라미터 주위의 괄호와 화살표를 생략할 수 있다는것이다.
인라인 클로저를 함수의 파라미터로 전달할때, 클로저의 파라미터 타입과 반환값 타입은 항상 유추할 수 있다. 따라서 인라인 클로저를 완전한 형태로 작성할 필요가 없다.
인라인 클로저내에서 단일표현식을 사용할땐 return 또한 생략할 수 있다.
축약형 이름
Swift는 인라인 클로저에 자동으로 축약형 이름을 제공한다. $0, $1... 로 표현되며 클로저의 파라미터를 참조하는데 사용될 수 있다.
축약형 인수 이름을 사용하였을때, 클로저의 파라미터 목록을 생략할 수 있다.
축약형 인수 번호는 0번부터 시작해서 가장 높은 수의 번호의 인수에 따라 클로저가 취하는 파라미터 개수가 결정된다.
클로저 표현식 자체가 전체 본문으로 구성되어 있기 때문에 축약형을 사용했을땐 in 키워드를 생략할 수도 있다
연산자 함수를 사용한 클로저의 더 짧은 표현식 나타내기
위 예시에서 sorted 함수는 두개의 String타입 파라미터를 갖고 Bool 타입의 반환값을 가지는 클로저를 파라미터로 가질때, sorted 파라미터는 > 연산자 함수와 완벽하게 일치한다. 그러므로 해당 클로저는 > 연산자 함수 하나로 대체할 수 있다.
후행 클로저
클로저 표현식을 함수의 마지막 파라미터로 전달해야하고, 클로저 표현식이 긴 경우 후행클로저로 작성하는 것이 유용할 수 있다.
후행클로저는 파라미터임에도 불구하고 함수 호출 바로 뒤 위치에 작성한다.
후행클로저는 클로저가 길어서 한줄로 작성할 수 없을때 가장 유용하다.
후행클로저를 작성할땐 파라미터로 들어가는 클로저의 이름을 생략할 수 있다. 여기서 생략하지 않으면 함수 호출을할때 파라미터 클로저는 후행클로저도 될 수 있고 인라인 클로저도 될 수 있다.
해당 함수는 클로저 파라미터는 아래와 같이 인라인 클로저로 사용되는 형식으로 호출될 수 있고
아래와 같이 후행클로저로 사용되는 형식으로 호출될 수 있다.
위 예시의 sorted 함수 또한 파라미터가 마지막 클로저이므로 후행클로저로 사용될 수 있다. (by가 생략됨)
또한 함수의 파라미터가 유일하게 하나의 클로저일때, 함수를 호출할 때 괄호를 생략할 수 있다.
함수의 인자로 클로저가 여러개 들어갈때, 다중 후행클로저 문법으로 첫번째 클로저 인수에 대한 argument를 생략하고 그 이후의 클로저 인수에 argument를 표시하여 클로저를 제공할 수 있다.
위 예시의 loadPicture 함수는 두개의 클로저를 인수로 받으며, loadPicture 함수를 호출할땐 다음과 같이 다중 후행 클로저 문법으로 나타낼 수 있다.
아래는 함수가 클로저를 여러 인수로 받을때 호출 예시이다.
Capturing Values
클로저는 클로저가 정의된 곳 주변 문맥에서 상수 또는 변수를 캡처할 수 있다. 그렇게 되면 클로저는 상수, 변수를 정의한 원래 범위가 더이상 존재하지 않는 경우에도 클로저 본문 내에서 해당 상수, 변수 값을 참조하고 수정할 수 있다.
Swift에서 값을 캡처할 수 있는 가장 간단한 형태의 클로저는 다른 함수 본문 내에 작성된 중첩 함수이다.
makeIncrementer 함수는 incrementer 이라는 중첩함수를 가지고 있고, incrementer() 함수는 runningTotal 과 amount 두개의 값을 캡처한다.
다음은 작동 중인 makeIncrementer의 예이다.

이 incrementByTen 함수를 여러번 호출하게 되면 해당 함수의 리턴값이 10씩 증가하여 실행되는것을 볼 수 있다.
여기서 리턴값은 runningTotal 로 캡처된 수에 10이 호출될때마다 누적되어 리턴된다.
여기서 두번째 incrementer 함수를 만들게 된다면, 새로운 runningTotal 변수에 대한 참조가 생기게 된다.
클로저는 참조 유형이다.
위의 예시에서 alsoIncrementByTen와 incrementByTen는 동일한 클로저를 참조하기 때문에 호출할때마다 동일한 값을 증가시키고, 동일한 값을 반환한다.
Escaping Closure
클로저는 함수에 인수로 전달될 때 함수를 탈출한다고 말하지만 함수가 종료된 후에 호출된다.
함수가 종료된 후에 클로저를 호출할 때, 파라미터에 @escaping을 작성하여 클로저가 이스케이프 할 수 있음을 나타낸다.
completionHandlers 배열에 클로저를 someFuntionWithEscapingClosure 함수를 통해 추가하려할 때, 추가하려는 클로저는 함수가 끝난 이후에 실행되어야 하므로 @escaping 키워드를 붙여 함수가 종료된 이후에 실행될 클로저임을 나타낼 수 있다.
self를 참조하는 이스케이프 클로저는 self가 클래스의 인스턴스를 참조하는 경우 조심해야한다. 순환참조를 야기할 수 있기 때문이다.
일반적으로 클로저는 클로저 본문에서 변수를 사용하여 암시적으로 변수를 캡처하지만 escaping 클로저에서는 명시적이어야 한다.
escaping closure 내에서 캡처하고자 하는 리스트를 표시하여 명시할 수 도 있다.
여기서 캡쳐하려는 값들을 대괄호 안에 넣으면 각 변수나 상수마다 self를 명시해주지 않아도 된다.
왜 escaping closure는 self를 명시해야만 할까?
클로저는 클로저가 캡처하는 모든 객체에 대한 strong 참조를 유지한다. 여기엔 클로저 내의 프로퍼티, 인스턴스 메서드에 접근하는 경우의 self가 포함된다.
이런식으로 순환참조를 마주치게 되는건 쉬운일이게 된다. 이것이 컴파일러가 클로저 내부에서 명시적으로 self를 사용하여 참조하도록 하는 요구하는 이유이다. 이렇게 하면 잠재적인 순환참조에 대해 생각하고 캡처목록을 사용하여 순환참조를 방지할 수 있게된다.
하지만 escaping 클로저가 아닌 클로저가 순환참조를 만드는 것은 불가능하다.
컴파일러는 함수가 종료될때까지 클로저가 캡처한 모든 객체를 해제하도록 보장하기 때문이다.
이러한 이유로 컴파일러는 escaping closure에게만 self를 사용하여 명시적인 참조를 하도록 요구한다.
Autoclosures