📸 Capture
Swift 공식문서에 다음과 같은 구문이 있다.
A closure can capture constants and variables
from the surrounding context in which it's defined.
클로저는 주변의 컨텍스트에 있는 상수나 변수들을 캡쳐할 수 있다.
상수와 변수를 정의한 원래 범위가 더 이상 존재하지 않더라도
그 상수와 변수의 값을 참조하고 수정할 수 있다.
🪺 Nested Function
Swift 에서 값을 캡쳐할 수 있는 클로저의 가장 간단한 형태는
중첩함수 [ Nested Function ] 이다.
이는 다른 함수의 body 안에 정의되어 있다.
중첩 함수는 외부 함수의 인수를 캡쳐할 수 있으며
또한 외부 함수 내에 정의된 상수와 변수를 캡쳐할 수 있다.
// example1
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount // runningTotal과 amount를 캡처
return runningTotal
}
return incrementer
}
내부의 incrementer
함수는
변수 runningTotal
과 amount
의 값을 캡쳐하고 있다.
이 값들을 캡쳐한 후, incrementer
함수가 호출될 때마다
runningTotal
에 amount
값을 더하여 반환하는 클로저로,
makeIncrementer
의 반환 값으로 사용되고 있다.
함수 makeIncrementer
는 반환타입이 () -> Int
이다.
이는 함수를 반환한다는 것을 의미한다.
반환되는 함수는 매개변수가 없으며 호출될때마다 Int
값을 반환한다.
만약 위의 중첩형태를 중첩하지 않는다면 어떻게 될까?
// example2
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
함수 incrementer
에 아무런 매개변수가 없기 때문에
변수 runningTotal
과 amount
는 참조값을 캡쳐한 것이다.
참조값을 캡쳐한다는 것은 함수 makeIncrementer
가 종결되어도
함수 incrementer
가 다음에 또 호출되더라도
변수 runningTotal
과 amount
의 값은 사라지지 않는다는 것을 보장한다.
Swift 는 해당 값이 클로저에 의해 변형되지 않고
클로저가 생성된 후에 값이 변형되지 않는다면
값의 복사본을 캡쳐하여 저장할 수 있도록 최적화되어 있다.
❗️ Points to note
만약 클래스 인스턴스의 프로퍼티로 클로저를 할당하고
해당 인스턴스 또는 해당 인스턴스의 멤버 변수들을 참조하여
해당 인스턴스를 캡쳐하는 경우,
클로저와 인스턴스 사이에 강한 참조 사이클이 발생한다.
Swift 는 캡쳐 목록을 이용하여 강한참조를 해제한다.
📚 Reference
Closures
Swift: Closure와 Capturing
[Swift] Capturing Values 값을 캡쳐한다는 것