예제

그럼 아래 처럼 했을 것이다. 과연 될까?

-
안된다.

-
이 아래 람다식 내부에서 외부 변수 i를 사용하는 순간 해당 변수는 클로저로 캡쳐 된다.
-
반복문으로 i값이 업데이트 되는데 i를 람다식에서 사용하는 순간 클로저 현상이 발생한다. i = 0 일때만이 아니라, 모든 i = 0,1,2에서 동일하게 발생한다.
-
또한 람다식은 이벤트 "등록"만 하지, 이벤트 호출은 "클릭" 했을 때만 실행된다. 즉, for문에서 람다식은 이벤트 등록을 반복적으로 수행 중이다.
-
클릭했을 때는 이미 for문이 실행 종료 되었을 것이다. 종료가 되면 람다식은 마지막 i 변수에 대한 기억만 하고 있기 때문에 i 변수의 마지막 값인 3을 기억하게 된다.

클로저: 함수가 자신이 만들어졌던 그 순간의 변수 상태를 기억하고 있는 것
나중에 함수가 호출될 때도 그 변수에 접근할 수 있게 해주는 기능
- 반복문을 통해 람다함수나 델리게이트 함수를 사용하게 되면 생기는 문제다.
- for문에서 int i = 0;을 선언했을 때 i는 매 루프마다 새로 만들어지는 것이 아니라 같은 i의 값을 범위까지 업데이트 하고 있는 것이다.
- 람다식 () => ChangeDigit(i,1)은 i값을 복사 하는 것이 아니라 i 변수를 기억하는 것이다. 따라서, 루프가 끝난 후 최종값인 3을 보게 된다.
- 람다식은 값을 복사하지 않고 변수 자체를 캡쳐한다. 즉, 변수의 주소 자체를 저장한다. for문이 끝난 후에 i가 3이므로, 모두 같은 i 변수를 캡처 하고 있기 때문에 , 결과적으로 같은 변수 하나를 공유한다.

i < 3; 조건이 있는데 최종값인 3을 어떻게 보는걸까?
- i = 2 일 때 i++으로 i = 3이 되고, i < 3; 조건으로 인해 반복을 수행하지 않는다. 결국 마지막 값은 i = 3 이다.
- 3이 클로저로 인해 i는 항상 3이 될 것이다.
클로저 예제( 클로저는 "값을 "복사 하는게 아니라 "변수"를 캡쳐 한다. (변수의 주소를 캡쳐)
- 이벤트를 i가 0일 때 등록을 하였지만, 실제 이벤트를 실행하면 마지막 i의 값을 출력. 클로저는 값 타입이든 참조 타입이든 변수 자체를 기억한다.


해결 방법 : 반복 마다 새로운 변수를 추가하여 서로 다른 변수를 캡쳐 하는 방식
for(int i = 0; i < 3; i++)
{
int index = i; //캡쳐를 i가 아닌 index로 수행.
TimeSelectUpButtons[2 - index ].onClick.AddListener(() => ChangeDigit(index, 1));
TimeSelectDownButtons[2 - index].onClick.AddListener(() => ChangeDigit(index, -1));
}
- 반복마다 index라는 새로운 변수가 생성됨 → 1회차 index(0), 2회차 index(1), 3회차 index(2)
- 각 람다는 다른 index 변수를 캡쳐함. -> 반복마다 새로운 변수를 생성했기에 변수 주소 위치가 다름.

