EditorStyles null Reference

문제점

Editor를 상속받은 클래스의 OnEnable에서 GUIStyle을 사용하려면 NullReference가 발생한다. 무조건은 아니지만, 다시 리로드되는 타이밍에는 직렬화 되지 않은 데이터는 초기화되므로 무조건 다시 NullReference가 발생하는 것 같다.

private void OnEnable()
{
    Init();
    //SOManagers.instance.hexGridGenerator = new HexGridGenerator(_hCGeneration.HexPrefab);
}

private void Init()
{
    _mainHeaderStyle = new GUIStyle(EditorStyles.boldLabel)
    {
        fontSize = 18, // 원하는 폰트 크기
        fixedHeight = 30f, // 원하는 높이
        alignment = TextAnchor.MiddleLeft // 텍스트 좌측 정렬
    };

    _subHeaderStyle = new GUIStyle(EditorStyles.boldLabel)
    {
        fontSize = 15, // 원하는 폰트 크기
        fixedHeight = 20f, // 원하는 높이
        alignment = TextAnchor.MiddleLeft // 텍스트 좌측 정렬
    };
}

 

이는 유니티가 싱글톤처럼 다양한 editor style을 관리하기 떄문인데, 안타깝게도 lazy insntantiate가 되진 않는다. 초기화를 처리하는 UpdateSkinCache라는 내부 메서드가 있는데, 이 메서드는 Unity 에디터에서 특정 시점에 자동으로 호출된다.

// UpdateSkinCache
internal static void UpdateSkinCache(int skinIndex)
{
    if (GUIUtility.s_SkinMode != 0)
    {
        if (s_CachedStyles[skinIndex] == null)
        {
            EditorResources.RefreshSkin();
            s_CachedStyles[skinIndex] = new EditorStyles();
            s_CachedStyles[skinIndex].InitSharedStyles();
        }

        s_Current = s_CachedStyles[skinIndex]; // s_Current가 EditorStyle에서 사용하는 싱글톤 변수이다.
        EditorGUIUtility.s_FontIsBold = -1;
        EditorGUIUtility.SetBoldDefaultFont(isBold: false);
    }
}

 

ScriptableObjects(Custom Editor 또는 Editor windows도 모두 스크립터블 오브젝트)의 OnEnable은 오브젝트가 로드되거나 역직렬화 될 때 바로 호출된다. 따라서 스타일 초기화를 지연시켜 OnGUI / OnInsepctorGUI, OnSceneGUI에서 실제로 필요할 때 수행하도록 해야한다.

 

해결 방법 1

스타일 초기화를 지연시켜 OnInspectorGUI에서 실제로 필요할 때 호출을 실행할 때는 이미 EditorGUIStyle에 대해 초기화가 끝났을 것이므로 직접적으로 OnInspectorGUI에서 _mainHeaderStyle등을 바로바로 직접 초기화 하거나, 아니면 별도의 클래스로 Style을 관리하여, OnInsepctorGUI안에서 접근만 하도록 만들면 NullReference가 발생할 일은 없을 것이다.

 

 

해결 방법 2

간단하게 구현하려면 OnInsepctorGUI에서 스타일 초기화를 그냥 수행하면 되겠지만, OnInspectorGUI 특성 상 여러번 반복 호출되기에 조금 더 깔끔하게 해결하고 싶다. 이를 해결하기 위한 아주 깔끔한 해결책은 사용자 정의 스타일을 관리하기 위해 자신만의 클래스를 만들고, 지연 초기화 기능을 갖춘 싱글톤으로 구현하는 것이다. 지연 초기화는 잘못된 위치에서 싱글톤을 사용할 때 "약간"위험하지만 보통은 잘 작동한다고 한다,

 

 

참고 자료