Unity Instantiate / AddComponent 의 Awake / OnEnable 호출시점

OnEnable의 Null 에러

스크립트의 OnEnable에서 레퍼런스를 사용한 동작을 수행하는 경우 주의하지 않으면 null 오류가 발생하는 경우를 자주 볼 수 있다. 문제는 대부분 OnEnable의 호출시점 때문.

특히나 Monobehaviour는 생성자가 없기 때문에 생성한 다음 메서드를 호출하여 초기화를 하는 경우가 생긴다.

OnEnable이 외부 호출된 메서드보다 먼저 호출되기 때문에 오류가 발생할 여지가 다분하다.

 

OnEnable의 호출 시점

메인 오브젝트에 스크립트 2개가 붙어있는 상황에서

A 스크립트 (빨간색), B 스크립트 (노란색)

빨간색이 Awake에서 C 스크립트 (초록색)를 AddComponent 한 뒤,

Debug.Log("<color=red> A Instantiate </color>");
var testPrefabScript = Instantiate(testPrefab);
testPrefabScript.SomeFunction();

SomeFunction을 호출하도록 요청했을 때 아래와 같은 순서로 실행된다.

노란색과 빨간색의 Awake와 OnEnable 시점에 주의하자.

A와 B의 Awake가 호출된 뒤에 OnEnable이 호출되는 게 아니라, 각각이 Awkae - OnEnable 순서로 실행된다.

 

A(빨간색)의 Awake는 AddComponent한 C(초록색)의 동작이 끝난 다음에 리턴되기 때문에

OnEnable은 그 후에 호출된다. 

 

프리팹의 Instantiate도 호출 시점이 AddComponent와 동일한 것을 확인함.

만약 생성되는 스크립트가 2개라면? 아래와 같이 진행된다.

 

결론

결론적으로 OnEnable에서 레퍼런스를 가지고 동작을 수행하는 경우 OnEnable의 호출 시점에 주의해야 한다.

 

Monobehaviour는 생성자가 없기 때문에 외부에서 레퍼런스를 주입 혹은 특정 변수의 초기화 등의 이유로 별도로 초기 화용 메서드를 만들어서 사용하는 경우가 있다. 

나의 경우 초기화 메서드를 만들어 뒀으니 GameObject 내부에서 GetComponent<>를 사용하여 Button이나 Image 등의 레퍼런스를 받아오는 부분을 합쳐두는 게 보기에 좋겠지라며 생각 없이 위치를 옮겼다가 OnEnable에서 문제가 발생하였다.

(OnEnable에서 Image의 투명도를 0으로 만들어주는 다른 변수의 초기화와 관계없는 동작이었다. 플래그를 세우고 초기화 메서드 후에 OnEnable 내부를 다시 호출하는 방법 등을 생각해볼 수 있다.)

 

아무튼 내부의 GetComponent 등은 OnEnable의 이전에 호출되는 것이 보장되는 Awake에서 초기화를 권장한다.

 

 

댓글

Designed by JB FACTORY