클로저는 정의된 둘러싸인 컨텍스트에서 상수와 변수를 캡처 (capture) 할 수 있다. 그러면 클로저는 상수와 변수를 정의한 원래 범위가 더이상 존재하지 않더라도 본문 내에서 해당 상수와 변수의 값을 참조하고 수정할 수 있다.
Swift에서 값을 캡처할 수 있는 가장 간단한 클로저 형태는 다른 함수의 본문 내에 작성하는 중첩 함수이다. 중첩 함수는 바깥 함수의 어떠한 인수도 캡처할 수 있고 바깥 함수 내에 정의된 상수와 변수를 캡처할 수도 있다.
아래는 incrementer 라는 중첩 함수가 포함된 makeIncrementer 라는 함수의 예입니다. 중첩된 incrementer() 함수는 둘러싸인 컨텍스트에 runningTotal 과 amount 인 2개의 값을 캡처합니다. 이 값을 캡처한 후에 incrementer 는 호출될 때마다 amount 로 runningTotal 을 증가시키는 클로저로 makeIncrementer 에 의해 반환됩니다.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
makeIncrementer 의 반환 타입은 () -> Int 이다. 이것은 단순한 값이 아닌 함수 를 반환한다는 의미이다. 반환하는 함수에는 파라미터가 없으며 호출될 때마다 Int 값을 반환한다.
makeIncrementer(forIncrement:) 함수는 반환될 현재 증가분을 저장하기 위해 runningTotal 이라는 정수 변수를 정의한다. 이 변수는 0 으로 초기화 됩니다.
makeIncrementer(forIncrement:) 함수는 forIncrement 의 인수 라벨의 하나의 Int 파라미터를 가지고 amount 라는 파라미터 이름을 가지고 있습니다. 이 파라미터에 전달된 인수값은 반환된 증가 함수가 호출 될 때마다 runningTotal 을 얼마나 증가시켜야 하는지 지정한다. makeIncrementer 함수는 실제 증가를 수행하는 incrementer 라는 중첩 함수를 정의한다. 이 함수는 간단하게 amount 를 runningTotal 에 더하고 그 결과를 반환한다.
단독으로 고려할 때 incrementer() 함수는 비정상적으로 보일 수 있다:
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
incrementer() 함수는 파라미터가 없으며 함수 본문 내에 runningTotal 과 amount 를 참조하고 있다. 둘러싸인 함수에 runningTotal 과 amount 대한 참조 (reference) 를 캡처하고 함수 내에서 사용한다. 참조를 캡처하는 것은 makeIncrementer 호출이 종료될 때 runningTotal 과 amount 가 사라지지 않고 다음에 incrementer 함수가 호출될 때 runningTotal 을 사용할 수 있다는 것을 의미한다.
Note
최적화로 Swift는 값이 클로저에 의해 변경되지 않고 클로저가 생성된 후 값이 변경되지 않는 경우 값의 복사본을 캡처하고 저장할 수 있다.
Swift는 더이상 필요하지 않을때 변수를 처리하는 것과 관련된 모든 메모리 관리도 처리한다.
다음은 makeIncrementer 동작에 대한 예이다:
let incrementByTen = makeIncrementer(forIncrement: 10)
이 예제에서는 호출될 때마다 runningTotal 변수에 10 을 더하는 증가 함수를 참조하도록 incrementByTen 이라는 상수를 설정한다. 함수를 여러번 호출하면 이 동작이 수행되는 동작을 보여준다:
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
두번째 증가를 생성하면 새로운 분리된 runningTotal 변수에 참조 저장된다:
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
기존 증가 (incrementByTen)을 다시 호출하면 그것의 runningTotal 변수는 이어서 증가되고 incrementBySeven 으로 캡처된 변수는 영향을 주지 않는다:
incrementByTen()
// returns a value of 40
Note
클래스 인스턴스의 프로퍼티에 클로저를 할당하고 클로저가 인스턴스 또는 멤버를 참조하여 해당 인스턴스를 캡처하면 클로저와 인스턴스 사이에 강한 참조 사이클이 생성된다. Swift는 캡처 리스트를 사용하여 이러한 강한 참조 사이클을 깨뜨린다.