본문 바로가기
잡다한 지식(Unity, C#)

대리자 함수 등록과 제거 (람다식)

by LemongO 2024. 6. 11.

Q. Delegate, Action, Func 를 사용 시, 콜백함수를 등록할 때 과연 람다식으로 작성한 함수도 제거가 될까?

 

개인 공부를 하다가 한 가지 의문이 들었다.
Manager 클래스를 통해 상태변화를 알려주는 콜백 함수를 Action에 등록하고 제거하는 과정에서 보통 이런 콜백 함수는 람다식으로 쓰는 경우가 자주 있는데, 등록은 그렇다 치고 제거도 람다식으로 가능한가?

평소 작업하던 방식은 Action 자체를 null로 밀어버리는 방식을 썼는데
등록한 클래스에서 람다식으로 등록 및 제거를 하는 방식이 가능한지 의문이 들었다.

 

 

using System;
using UnityEngine;

public class ActionTest : MonoBehaviour
{
    public static Action OnMove;

    public void MoveBtn()
    {
    	Debug.Log("버튼누름");
        OnMove?.Invoke();        
    }
}

 

using UnityEngine;

public class TestOBJ : MonoBehaviour
{
    private void Start()
    {
        ActionTest.OnMove += () => transform.position += Vector3.right;

        Destroy(gameObject, 5f);
    }

    private void OnDestroy()
    {
        ActionTest.OnMove -= () => transform.position += Vector3.right;
    }
}

 

테스트를 위해 만든 두 클래스.

ActionTest 클래스는 static Action을 가지고 있고 MoveBtn 함수를 버튼에 등록해 버튼이 눌리면 함수가 호출 되도록 만들었다.

TestOBJ 클래스는 Circle 오브젝트에 부착된 컴포넌트로 시작 시 OnMove에 람다함수를 등록.

파괴 시 같은 내용의 람다 함수를 제거 하는 방식을 사용한다.

 

단순히 버튼을 누르면 오른쪽으로 이동하는 함수이다.

 

 

자 그럼 5초 후, 오브젝트가 사라진 상태에서 버튼을 다시 눌러보자.

 

MissingReferenceException: The object of type 'TestOBJ' has been destroyed but you are still trying to access it.

 

즉시 오류가 떠버리는 모습이다. 오브젝트가 파괴되었으나 접근을 시도한다고 한다.

 

 

????????????????????????????????

된다며...

????????????????????????????????

 

 

using UnityEngine;

public class TestOBJ : MonoBehaviour
{
    private void Start()
    {
        ActionTest.OnMove += MoveMethod;

        Destroy(gameObject, 5f);
    }

    private void OnDestroy()
    {
        ActionTest.OnMove -= MoveMethod;
    }

    private void MoveMethod() => transform.position += Vector3.right;
}

 

함수 등록/해제를 일반 함수로 바꿔서 해보자.

 

 

 

잘 된다.

 

 

버튼 UI를 쓰는김에 UI에도 적용해보자.

 

using UnityEngine;
using UnityEngine.UI;

public class ActionTest : MonoBehaviour
{
    public Button btn;    

    private void Awake()
    {
        btn.onClick.AddListener(() => Debug.Log("버튼누름"));
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            Debug.Log("람다 제거");
            btn.onClick.RemoveListener(() => Debug.Log("버튼누름"));
        }            
    }
}

 

제거가 안 된다.

 

using UnityEngine;
using UnityEngine.UI;

public class ActionTest : MonoBehaviour
{
    public Button btn;    

    private void Awake()
    {
        btn.onClick.AddListener(RemoveMethod);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            Debug.Log("람다 제거");
            btn.onClick.RemoveListener(RemoveMethod);
        }            
    }

    private void RemoveMethod() => Debug.Log("버튼누름");
}

마찬가지로 일반 함수로 해보자.

 

역시나 잘 된다.

결론 : 람다 함수는 대리자에 등록만 된다.