값 캡쳐링
클로져는 주위 문맥으로부터 상수와 변수를 캡쳐(capture)할 수 있습니다. 이후 클로져는 이 상수와 변수를 클로져 내부에서 참조하거나 수정할 수 있습니다. 심지어 원본의 상수와 변수가 더 이상 존재하지 않더라도 그렇습니다.
스위프트에서 값을 캡쳐링하는 클로져의 가장 간단한 형태는 중첩 함수입니다. 이 함수는 다른 함수 내부에 작성된 함수입니다. 중첩 함수가 바깥 함수의 인자 중 아무 값이나 캡쳐할 수도 있으며 바깥 함수에서 정의된 상수나 변수를 캡쳐할 수도 있습니다.
여기에 makeIncrementer
라고 정의된 함수가 있습니다. 이 함수는 중첩 함수, incrementer
를 가지고 있습니다. 중첩된 incrementer()
함수는 두 개의 값을 캡쳐합니다. 바로 runningTotal
과 amount
입니다. 이 값들을 캡쳐를 하고나면, incrementer
는 makeIncrementer
에 의해 반환됩니다. 이 함수는 클로져임 runningTotal
을 amount
만큼 증가합니다.
func makeIncrementer(forIncrementer amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
이 예제의 반환 타입은 () -> Int
입니다. 즉, 값을 반환하는 것이 아닌, _함수_를 반환합니다. 이 함수는 어떠한 파라미터도 갖지 않습니다. 대신, 한 번씩 호출될 때 마다 Int
값을 반환합니다. 함수가 어떻게 다른 함수를 반환하는 지에 대해서는 Function Types as Return Types을 참고하세요.
makeIncrementer(forIncrement:)
함수는 정수 값 runningTotal
을 정의하였습니다. 이 값은 현재 incrementer의 현재 총합을 저장하고 반환됩니다. 이 변수의 초기값은 0
입니다.
함수 makeIncrementer(forIncrement:)
는 하나의 Int
파라미터로 forIncrement
라는 인자 레이블을 갖습니다. 그리고 amount
라는 파라미터 이름을 갖습니다. 이 인자 값은 runningTotal
이 매번 호출 될 때, 얼만큼을 증가할 것인지를 정의합니다. 함수 makeIncrementer
는 실제의 값의 증가를 수행할 중첩 함수인 incrementer
를 정의합니다. 이 함수는 단순히 runningTotal
에 amount
값을 더하고 이 총합을 반환합니다.
중첩함수를 따로 떼어 놓으면 이상하게 보일 것 입니다.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
어떠한 파라미터도 incrementer()
함수는 갖고 있지 않습니다. 그리고, runningTotal
과 amount
를 이 함수의 내부에서 참조하고 있습니다. 참조 값runningTotal
과 amount
를 주변 함수로부터 캡쳐하고 이것을 자신의 함수 내부에서 사용하고 있습니다. 참조 캡쳐링은 runningTotal
과 amount
를 makeIncrementer
가 종료된 이후에도 사라지도록 하지 않습니다. 또한, runningTotal
은 incrementer
함수가 다음에 호출될 때에도 여전히 남아 있습니다.
최적화와 관련하여, 스위프트는 값의 복사본(a copy of a value)을 캡쳐하고 저장합니다. 단, 그 값이 클로져에 의해 변경되지 않아야 하며 그 값은 클로져가 생성된 이후에도 변경되지 않아야 합니다.
스위프트는 더 이상 사용하지 않는 변수를 처분하는 등의 모든 메모리 관리를 다룹니다.
여기에 makeIncrementer
를 사용한 예제가 있습니다.
let incrementByTen = makeIncrementer(forIncrement: 10)
이 예제는 상수인 incrementByTen
을 증가 함수를 참조하기 위해 설정하였습니다. 이 값은 runningTotal
변수를 호출할 때 마다 10씩 더합니다. 여러번 이 함수를 호출하면 다음과 같은 결과를 보입니다.
incrementByTen()
// 10을 반환
incrementByTen()
// 20을 반환
incremntByTen()
// 30을 반환
만약 두번째 incrementer를 생성하면, 고유의 참조를 가지게 되어 새로운 runningTotal
변수가 설정됩니다.
let incrementBySeven = makeIncrementer(forIncrement:7)
incrementBySeven()
// return a value of 7
위에서 선언한 두 함수 incrementByTen
과 incrementBySeven
은 서로 영향을 받지 않습니다. 각자 개별적인 참조값을 가지게 됩니다.
참고
만약 여러분이 클로져를 어떤 클래스 인스턴스의 속성으로 할당하면, 클로져는 그 함수를 참조하여 인스턴스를 캡쳐합니다. 따라서, 여러분은 클로져와 인스턴스 사이에 강한 참조 싸이클을 생성하게 됩니다. 스위프트는 _리스트(list)_를 캡쳐하여 이러한 상한 참조 싸이클을 해제합니다. 더 자세한 정보는 Strong Reference Cycles for Closures 를 참고하세요.
원본 : https://docs.swift.org/swift-book/LanguageGuide/Closures.html
'Swift > Swift Language' 카테고리의 다른 글
Swift 언어 가이드 - 서브스크립트 Subscript (0) | 2020.04.12 |
---|---|
Swift 언어 가이드 - 클로져는 참조 타입이다 (0) | 2020.03.08 |
Swift 언어 가이드 - 트레일링 클로져 (0) | 2020.03.05 |
Swift 언어 가이드 - 클로져 기본 (0) | 2020.03.05 |
Swift 언어 가이드 - 클로져 표현식 (0) | 2020.02.22 |