🔥 클로저와 값 캡처
Swift에서 클로저는 정의된 주변 컨텍스트에서 상수와 변수를 캡처(capture)할 수 있습니다. 클로저는 원래 상수와 변수를 정의한 스코프가 더 이상 존재하지 않더라도 캡처한 상수와 변수를 참조하고 수정할 수 있죠.
Swift에서 값을 캡처할 수 있는 가장 간단한 형태의 클로저는 바로 중첩 함수(nested function)입니다. 중첩 함수는 다른 함수의 내부에 작성되며, 외부 함수의 인자와 외부 함수 내에 정의된 상수 및 변수를 캡처할 수 있어요.
아래는 makeIncrementer
라는 함수의 예시인데요, 이 함수는 incrementer
라는 중첩 함수를 포함하고 있습니다. 중첩된 incrementer()
함수는 주변 컨텍스트에서 runningTotal
과 amount
라는 두 개의 값을 캡처합니다. 이 값들을 캡처한 후, incrementer
는 makeIncrementer
에 의해 클로저로 반환되고, 호출될 때마다 runningTotal
을 amount
만큼 증가시키죠.
func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 // 현재까지 증가된 값을 저장할 변수 func incrementer() -> Int { runningTotal += amount // amount만큼 runningTotal을 증가시킴 return runningTotal // 증가된 runningTotal 값을 반환 } return incrementer // incrementer 함수 자체를 반환 }
swift
makeIncrementer
의 반환 타입은 () -> Int
입니다. 이는 단순한 값이 아닌 함수를 반환한다는 의미이죠. 반환되는 함수는 매개변수가 없으며, 호출될 때마다 Int
값을 반환합니다. 함수가 다른 함수를 반환하는 방법에 대해 더 알고 싶다면 Function Types as Return Types를 참고해 보세요.
makeIncrementer(forIncrement:)
함수는 반환될 incrementer의 현재 누적 값을 저장할 runningTotal
이라는 정수 변수를 정의합니다. 이 변수는 0
으로 초기화되어 있어요.
makeIncrementer(forIncrement:)
함수는 forIncrement
라는 인자 레이블과 amount
라는 매개변수 이름을 가진 단일 Int
매개변수를 가집니다. 이 매개변수에 전달된 인자 값은 반환된 incrementer 함수가 호출될 때마다 runningTotal
을 얼마나 증가시킬지 지정하죠. makeIncrementer
함수는 실제 증가 작업을 수행하는 incrementer
라는 중첩 함수를 정의합니다. 이 함수는 간단히 amount
를 runningTotal
에 더하고 결과를 반환해요.
고립된 상태에서 본다면 중첩된 incrementer()
함수는 좀 특이해 보일 수 있습니다:
func incrementer() -> Int { runningTotal += amount // 외부 함수의 변수들을 캡처해서 사용 return runningTotal }
swift
incrementer()
함수는 매개변수가 없지만, 함수 본문 내에서 runningTotal
과 amount
를 참조하고 있어요. 이는 주변 함수로부터 runningTotal
과 amount
에 대한 참조를 캡처하고 자체 함수 본문 내에서 사용하기 때문에 가능한 일이죠. 참조로 캡처하면 makeIncrementer
호출이 끝나더라도 runningTotal
과 amount
가 사라지지 않고, 다음에 incrementer
함수가 호출될 때 runningTotal
을 사용할 수 있게 해줍니다.
아래는 makeIncrementer
가 실제로 작동하는 예시에요:
let incrementByTen = makeIncrementer(forIncrement: 10)
swift
이 예시는 incrementByTen
이라는 상수를 정의하여 호출될 때마다 runningTotal
변수에 10
을 더하는 incrementer 함수를 참조하도록 합니다. 이 함수를 여러 번 호출하면 동작 방식을 확인할 수 있어요:
incrementByTen() // 10 반환 incrementByTen() // 20 반환 incrementByTen() // 30 반환
swift
두 번째 incrementer를 만들면, 새롭고 별도의 runningTotal
변수에 대한 자체 저장된 참조를 갖게 됩니다:
let incrementBySeven = makeIncrementer(forIncrement: 7) incrementBySeven() // 7 반환
swift
원래의 incrementer(incrementByTen
)를 다시 호출하면 incrementBySeven
에 의해 캡처된 변수에 영향을 주지 않고 자체 runningTotal
변수를 계속 증가시킵니다:
incrementByTen() // 40 반환
swift
이렇게 Swift의 클로저는 주변 컨텍스트에서 상수와 변수를 캡처할 수 있습니다. 이를 통해 클로저 내부에서 외부 스코프의 값들을 유연하게 활용할 수 있죠. 캡처된 값들은 클로저가 정의된 원래 스코프가 사라져도 클로저 내에서 계속 유지되고 참조될 수 있습니다.
또한 캡처는 값 타입과 참조 타입에 따라 동작 방식이 달라져요. 값 타입(value type)은 값 자체가 캡처되어 복사되는 반면, 참조 타입(reference type)은 참조가 캡처되어 원본 인스턴스를 공유하게 됩니다.
캡처의 개념을 잘 이해하면 클로저를 매우 강력하고 표현력 있게 사용할 수 있으니, 꼭 숙지해 두시는 게 좋겠죠? 클로저와 캡처를 적절히 활용하면 코드의 가독성과 재사용성을 높일 수 있답니다!