클로져의 변수 캡쳐링

클로저의 변수 캡쳐링

클로저는 자신이 정의된 문맥에서 주변의 변수와 상수값을 캡쳐한다. 클로저는 캡쳐한 변수와 상수를 액세스할 수 있고, 변수의 경우 값을 변경하는 것도 자유롭다.1 또한 자신을 정의했던 원래의 스코프가 끝난 후에도 (함수 내에서 클로저를 정의하고 함수가 종료됨) 이 값들은 여전히 캡쳐되어 유효하게 사용될 수 있다.

함수는 이름이 있는 클로져

함수 그자체는 이름이 있는 클로저이고, 따라서 Swift에서의 함수는 객체와 동등하게 취급되는 일급객체 타입이 된다.

func makeIncrementor(forIncrement amount:Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return amount
    }
    return incrementor
}

위 코드에서 함수 makeIncrementor는 주어진 amount 만큼씩 증가하는 값을 내놓는 함수를 리턴한다.

let c = makeIncrementor(forIncrement: 10)
println(c())
// 10
println(c())
// 20

함수는 기본적으로 클로저이므로 c가 생성되는 시점의 makeIncrementor() 내부에서는 incrementor()runningTotalamount를 캡쳐한다. 비록 makeIncrementor()의 실행이 끝난 후에도 이 두 값은 c에 의해 참조되고 있어서 계속 유지된다. 다음의 코드를 계속 실행한다고 하면,

let d = makeIncrementor(forIncrement: 7)
println(d())
// 7
println(c())
// 30
println(d())
// 14

보시다시피 새로운 함수가 만들어질 때마다 값은 새롭게 초기화되고 캡쳐링된다.

이를 익명 클로저로 정의하면 다음과 같다.

func makeIncrementor(forIncrement amout:Int) -> () -> Int {
    var runningTotal = 0
    return { 
        runningTotal += amount
        return runningTotal
    }
}

  1. Swift의 클로져는 Objective-C의 블럭과 달리 __block 등의 변경자가 없더라도 기본적으로 var로 선언된 변수들은 캡쳐된 후 클로저내에서 변경될 수 있다. 이는 Swift의 클로저는 기본적으로 이름이 없는 함수이기 때문이다.