Closures #4
Captureing Values
- closure는 자신을 둘러싸고 context의 상수나 변수를 capture할 수 있다.
- closure는 상수나 변수가 생성된 원래의 scope가 더이상 존재하지 않더라도 closure body 내에서 그 상수나 변수의 값을 참조하거나 변경할 수 있다.
- Swift에서 capture values를 수행할 수 있는 가장 간단한 형식은 다른 함수 내에 작성된 중첩함수이다.
- 중첨 함수는 자신을 둘러싼 함수의 어떤 파라미터도, 그리고 그 함수 내에 정의된 어떤 상수나 변수도 capture할 수 있다.
// makeIncrementer 함수는 () -> Int 타입(파라미터가 없고 Int 타입을 리턴하는) 함수를 리턴한다.
func makeIncrementer(fotIncrement amount: Int) -> () -> Int {
var runningTotal = 0
// 내부에 정의된 중첩함수 incrementer는 외부 함수인 makeIncrementer의 리턴 타입과 동일한
// 타입이다.
// incrementer 함수는 외부 함수 makeIncrementer에서 정의된 변수 runningTotal에
// makeIncrementer 함수의 파라미터 amount를 더한 값을 리턴한다.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
- incrementer 함수는 아무런 파라미터를 받지 않고 capturing을 이용하여 이 함수를 둘러싼 makeIncrementer에서 선언된 runningTotal 변수와 makeIncrementer 함수의 파라미터인 amount를 자신의 body에서 사용한다.
- 이렇듯 capturing은 makeIncrementer 함수의 호출이 끝나더라도 runningTotal이나 amount는 사라지지 않고 다음에 incrementer함수가 호출되었을 때에도 사용할 수 있도록 보장해준다.
최적화를 위해 Swift는 closure에 의해 값이 변경되지 않았거나 closure 생성시 값이 변경되지 않았다면 복사본을 capture하고 저장한다.
또한 변수들이 더이상 사용되지 않을 경우의 메모리 배치를 위해 모든 메모리 관리자를 처리한다.
let increamentByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// 10을 리턴
incrementByTen()
// 20을 리턴
incrementByTen()
// 30을 리턴
- 두 번째 incrementer를 생성한다면 새로운 incrementer는 새로운 별개의 runningTotal 값을 저장하고 참조하게 된다.
let increamentBySeven = makeIncrementer(forIncrement: 7)
increamentBySeven()
// 7을 리턴
incrementByTen()
// 40을 리턴
closure를 class 인스턴스의 속성으로 할당하고, 이 closure가 그 클래스의 인스턴스나 클래스의 멤버를 capturing 하면 closure와 class instance 사이에 강한 첨조 주기가 만들어진다. 이러한 강한 참조 주기를 깨기 위해 Swift는 capture list를 이용한다.