[유니티 / C# ] 캡쳐(Capture)와 클로저(Closure)

⭐ 캡쳐와 클로저

람다식이나 익명 메서드가 자신이 선언된 스코프 밖에 있는 지역 변수의 값을 복사하는 것이 아닌 변수 자체를 잡아두는 것을 캡쳐라고 합니다.

위처럼 외부 변수를 캡쳐한 함수 그 자체를 클로저라고 부릅니다.

 

● 캡쳐 = 외부 변수를 잡아두는 행위

● 클로저 = 캡쳐가 발생한 변수

 

 

왜 일어날까?

 


● 람다식, 익명 메서드가 지역변수를 사용 후 사라져야 하지만 람다는 여전히 그 지역 변수를 필요로 합니다.

    ○ 컴파일러가 별도의 힙 객체를 만들어 참조

    ○ 람다, 익명 메서드가 살아있는 한 외부 변수가 GC로 수거되지 않고 값 변경도 그대로 반영됨 → 문제 발생

 

Action action;

private void Start()
{
    for(int i = 0; i < 5; i++)
    {
        action += () => Debug.Log(i);
    }

    action?.Invoke();
}

출력 결과:
5
5
5
5
5

● 분명 반복문을 통해 출력하고자 한 값은 0부터 4까지 출력되도록 하는 것이지만 캡쳐가 일어나 엉뚱한 결과가 출력됐습니다.

    ○ 반복문에서 선언한 변수 i에 캡쳐가 일어나 action에 들어간 람다식의 i들은 모두 같은 힙 메모리의 객체를 참조하고 있습니다.

    ○ 0부터 4까지 반복된 후 마지막으로 i++을 통해 i는 5가 되었기 때문에 모두 5가 출력됩니다.

 

● 위와 같은 문제를 해결하기 위해 따로 지역변수를 선언하여 넣어줍니다.

    ○ 반복문 내에 새로운 지역변수를 선언하여 해당 변수를 출력하도록 합니다.

 

해결 방법


Action action;

private void Start()
{
    for(int i = 0; i < 5; i++)
    {
        int temp = i;
        action += () => Debug.Log(temp);
    }

    action?.Invoke();
}

출력 결과:
0
1
2
3
4