클로저는 특정 문맥의 상수나 변수의 값을 캡쳐할 수 있습니다.
이는 원본 값이 사라져도 클로져의 body안에서 그 값을 활용할 수 있다는 이야기입니다.
Swift에서 값을 캡쳐 하는 가장 단순한 형태는 중첩 함수(nested function). 중첩 함수는 함수의 body에서 다른 함수를 다시 호출하는 형태로 된 함수입니다.
// 반환 값이 () -> Int 클로저인 함수 형태
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
위 함수에서 incrementer()만 따로 보면 runningTotal과 amount도 없지만, 이 함수는 잘 돌아갑니다.
그것은 runningTotal과 amount가 캡쳐링 되서 그렇습니다.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
(Swift는 만약 더 이상 클로저에 의해 값이 사용되지 않으면 그 값을 복사해 저장하거나 캡쳐링 하지 않습니다.
Swift는 또 특정 변수가 더 이상 필요하지 않을 때 제거하는 것과 관련한 모든 메모리 관리를 알아서 처리하도록 되어있습니다.)
클로저는 캡쳐할 때 기본적으로 참조 타입으로 캡쳐링을 합니다.
(일반적으로 값 타입(Int, Bool, Struct, ...)들은 값이 copy되어서, 참조 타입(Class)은 주소값을 저장하지만)
값 타입도 참조 타입도 클로저가 캡쳐하면 무조건 참조를 합니다.
(유사한 개념인 Objective-C의 블록은 값 타입은 copy, 참조 타입은 주소값을 참조합니다)
캡쳐리스트를 이용하면 달라지지만 이는 별도의 포스팅으로 정리하려 합니다.
위 중첩함수 makeIncrementer(forIncrement:)를 실행.
let incrementByTen = makeIncrementer(forIncrement: 10)
// makeIncrementer함수는 ()->Int 클로저를 반환.
// 여기서는 makeIncrementer 내부의 incrementer 함수를 실행하는 메소드를 반환
incrementByTen()
// 값으로 10을 반환
incrementByTen()
// 값으로 20을 반환
incrementByTen()
// 값으로 30을 반환
// 함수가 각기 실행 되지만
// 실제로는 변수 runningTotal과 amount가 캡쳐링 되서 그 변수를 공유하기 때문에
// 계산이 누적된 결과를 가짐
// 만약 아래와 같이 새로운 클로저를 생성하면?
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
// 다른 클로저이기 때문에 고유의 저장소에 runningTotal과 amount를 캡쳐링 해서 사용
// 그래서 다른 값이 나옴.
// 여기서 이전의 클로저를 다시 실행하면?
incrementByTen()
// 값으로 40을 반환
앞의 incrementByTen 클로저를 상수에 할당하고 실행시키면,
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// 사용한 클로저의 마지막 상태에서 10을 증가시켜 결과 값으로 50을 리턴
이는 클로저가 참조타입이기 때문에,
alsoIncrementByTen가 같은 incrementByTen을 참조하게 되고
makeIncrementer 내의 runningTotal의 마지막 상태를 참조하여 값을 증가시킵니다.
클로저는 특정 문맥의 상수나 변수의 값을 캡쳐할 수 있습니다.
이는 원본 값이 사라져도 클로져의 body안에서 그 값을 활용할 수 있다는 이야기입니다.
Swift에서 값을 캡쳐 하는 가장 단순한 형태는 중첩 함수(nested function). 중첩 함수는 함수의 body에서 다른 함수를 다시 호출하는 형태로 된 함수입니다.
// 반환 값이 () -> Int 클로저인 함수 형태
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
위 함수에서 incrementer()만 따로 보면 runningTotal과 amount도 없지만, 이 함수는 잘 돌아갑니다.
그것은 runningTotal과 amount가 캡쳐링 되서 그렇습니다.
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
(Swift는 만약 더 이상 클로저에 의해 값이 사용되지 않으면 그 값을 복사해 저장하거나 캡쳐링 하지 않습니다.
Swift는 또 특정 변수가 더 이상 필요하지 않을 때 제거하는 것과 관련한 모든 메모리 관리를 알아서 처리하도록 되어있습니다.)
클로저는 캡쳐할 때 기본적으로 참조 타입으로 캡쳐링을 합니다.
(일반적으로 값 타입(Int, Bool, Struct, ...)들은 값이 copy되어서, 참조 타입(Class)은 주소값을 저장하지만)
값 타입도 참조 타입도 클로저가 캡쳐하면 무조건 참조를 합니다.
(유사한 개념인 Objective-C의 블록은 값 타입은 copy, 참조 타입은 주소값을 참조합니다)
캡쳐리스트를 이용하면 달라지지만 이는 별도의 포스팅으로 정리하려 합니다.
위 중첩함수 makeIncrementer(forIncrement:)를 실행.
let incrementByTen = makeIncrementer(forIncrement: 10)
// makeIncrementer함수는 ()->Int 클로저를 반환.
// 여기서는 makeIncrementer 내부의 incrementer 함수를 실행하는 메소드를 반환
incrementByTen()
// 값으로 10을 반환
incrementByTen()
// 값으로 20을 반환
incrementByTen()
// 값으로 30을 반환
// 함수가 각기 실행 되지만
// 실제로는 변수 runningTotal과 amount가 캡쳐링 되서 그 변수를 공유하기 때문에
// 계산이 누적된 결과를 가짐
// 만약 아래와 같이 새로운 클로저를 생성하면?
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
// 다른 클로저이기 때문에 고유의 저장소에 runningTotal과 amount를 캡쳐링 해서 사용
// 그래서 다른 값이 나옴.
// 여기서 이전의 클로저를 다시 실행하면?
incrementByTen()
// 값으로 40을 반환
앞의 incrementByTen 클로저를 상수에 할당하고 실행시키면,
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// 사용한 클로저의 마지막 상태에서 10을 증가시켜 결과 값으로 50을 리턴
이는 클로저가 참조타입이기 때문에,
alsoIncrementByTen가 같은 incrementByTen을 참조하게 되고
makeIncrementer 내의 runningTotal의 마지막 상태를 참조하여 값을 증가시킵니다.