Closure - Capture List
저번 포스팅에서는 기본적인 문법부터 다양한 축약형의 문법 그리고 값으로써의 클로저를 알아보았습니다. 이번에는 클로저를 사용하면서 중요한 문법 중 하나인 Capture List
에 대해서 알아보도록 하겠습니다.
글을 읽기전에 먼저 숙지하고 계셔야할 주제들입니다.
Closure는 Reference Type이다.
기본적으로 클로저는 Reference Type
입니다.
이 포스팅에서는
Reference Type
과Value Type
의 차이점을 설명하지 않습니다.
또한 클로저가 매개변수로 값을 넘기는 것이 아닌 단순히 클로저 바깥의 값을 사용할 때는 값을 복사하여 클로저 내부에 저장하고 사용하는 것이 아닌 해당 값을 참조하여 사용하게 됩니다.
이러한 현상을 코드를 통해 확인해보도록 하겠습니다.
var a:Int = 5
var b:Int = 6
var closure = { print(a,b) }
closure() // output(1)
a = 0
b = 0
closure() // output(2)
var closure = { print(a, b) }
이 문법이 이해되지 않으시다면 제가 작성한 Closure[01] 포스팅을 먼저 보고 오시면 될 것 같습니다.
첫번째 출력의 결과로는 5와 6이 찍힐 것입니다. 두번째 출력의 결과로는 두개의 0이 찍힐 것입니다. 감이 오시나요?
네! 바로 클로저 내부의 a, b는 외부의 a,b를 참조하고 있는 것입니다. 이런식으로 값을 참조하게 된다면 참조하고 있는 값이 변경되면 참조하고 있는 곳에서의 값 역시 바뀌게 됩니다.
이러한 Reference Type의 특성으로인해 원치않는 값의 변화가 생길 수 있습니다.
문제점
다음 코드의 결과를 예상해보세요!
var index:Int = 0
var closureArr:[()->()] = []
for _ in 1...5 {
closureArr.append( {print(index)} )
index += 1
}
for i in 0...4 {
closureArr[i]()
}
이 코드는 실제로 자바스크립트를 공부하다보면 많이 보는 예제입니다.
겉으로 보기에는 충분히 결과가 보이는 코드인것 같습니다. 어떤 결과를 예상하셨나요?
0, 1, 2, 3, 4
로 예상하셨나요? 실제 결과는 5, 5, 5, 5, 5
입니다. 그 이유를 살펴보도록 하겠습니다.
for _ in 1...5 {
closureArr.append( {print(index)} )
index += 1
}
이 코드에서 index는 매개변수로 넘어가는 것이 아니라 외부에 있는 값이며 클로저 내부에서는 해당 변수를 참조하고 있습니다.
index
는 반복문을 돌면서 최종값으로는 5
가 될 것입니다.
즉 결국 클로저 내부에서 참조하고 있는 index의 값은 5이므로 결과 값이 5, 5, 5, 5, 5
가 되는 것입니다.
단지 이 예제뿐만 아니라 프로그램을 작성하다보면 생각보다 이러한 예상치 못한 결과값이 나오는 경우를 많이 보시게 되실 것입니다.
그러면 어떻게 이러한 문제를 해결하고 원하는 결과값을 얻을 수 있을까요?
Capture List
Capture List
, 이름만 들었을 때 감이 오시나요?
스크린 캡처라는 용어는 많이 들어보셨을거라 생각됩니다. 말 그대로 현재 스크린을 캡쳐하여 저정하는 것을 말합니다. 이러한 기능을 Swift에서는 Capture List
로 구현하고 있습니다.
일단 코드로 먼저 만나보시죠!
var index:Int = 0
var closureCaptureListArr:[()->()] = []
for _ in 1...5 {
closureCaptureListArr.append({ [index] in
print(index)
})
index += 1
}
for i in 0...4 {
closureCaptureListArr[i]()
}
위의 코드의 결과물은 0, 1, 2, 3, 4
가 됩니다. 이전의 코드와의 차이점이 보이시나요?
for _ in 1...5 {
closureCaptureListArr.append({ [index] in
print(index)
})
index += 1
}
네! 바로 매개변수로 넘어가는 클로저에서 약간의 차이가 존재합니다.
[index]
라는 코드가 추가가 되었습니다. 이를 해석해보자면 다음과 같습니다.
“ index
가 참조하는 값을 캡처하고 클로저 내부에 저장하여 값에 접근한다 ” 는 뜻입니다.
추가적으로 다음과 같이도 사용할 수 있습니다.
for _ in 1...5 {
closureCaptureListArr.append({ [item=index] in
print(item)
})
index += 1
}
바깥 변수와 같은 이름을 그대로 사용한다면 헷갈릴수도 있고 클로저 내부에서의 해당 변수의 역할을 제대로 표현하지 못하는 경우에는 다음과 같은 방법으로 변수명을 바꾸어 저장할 수 있습니다.
마무리
오늘은 클로저의 Capture List
에 대해서 알아보았습니다. 생각보다 이러한 모양을 한 문법을 많이 만나시는 경우가 있을실 것입니다. 이러한 문법을 숙지하시고 여러 샘플 코드들을 보시다보면 코드의 이해도도 높아지실 것입니다. 또한 클로저는 Reference Type
이라는 것도 중요한 점이니 반드시 이점 유의하며 코딩하시면 되시겠습니다. 감사합니다.
참고자료