개발자에게 최적화란 뗄래야 뗄 수 없는 존재인 거 같습니다. 오늘은 코루틴을 활용할 때의 최적화하는 방법에 대해 알아보겠습니다.
코루틴을 활용할 때 IEnumerator로 선언한 메서드는 다음과 같은 구문이 필요합니다.
yield return new WaitForSeconds(1.0f);
Unity의 생명주기와 무관하게 새로운 루틴을 실행할 수 있도록 가져온 제어권을 Unity에게 돌려주는 구문입니다. 이 때, WaitForSeconds에는 대체할 수 있는 여러 메서드들이 있습니다. 이번 TIL의 중점은 이 메서드의 종류가 아니기 때문에 생략하겠습니다...
이 때, new 키워드를 사용하기 때문에 인스턴스를 생성하게 됩니다. 그리고 이 인스턴스는 결국 가비지가 되어 가비지 컬렉터를 호출하게 되겠죠. 앞서 말씀드렸듯이 코루틴은 반복 실행될 경우가 많기 때문에 가비지 컬렉터가 굉장히 많이 호출될 것입니다. 그래서 캐싱을 많이 활용하곤 합니다.
WaitForSeconds watiForSeconds = new WaitForSeconds(1.0f);
그런데 프로젝트를 진행하다보면 여러 스크립트에서 코루틴을 활용할 일이 많고 중복된 값을 캐싱하는 경우가 계속 발생할 것입니다. 그래서 더 범용성 있게 static class를 하나 구현하였습니다.
public static class CoroutineHelper
{
public static readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame();
public static readonly WaitForFixedUpdate WaitForFixedUpdate = new WaitForFixedUpdate();
class Comparer : IEqualityComparer<float>
{
bool IEqualityComparer<float>.Equals(float x, float y)
{
return x == y;
}
int IEqualityComparer<float>.GetHashCode(float obj)
{
return obj.GetHashCode();
}
}
private static Dictionary<float, WaitForSeconds> _WaitForSeconds = new Dictionary<float, WaitForSeconds>(new Comparer());
public static WaitForSeconds WaitForSeconds(float seconds)
{
WaitForSeconds waitForSeconds;
if (!_WaitForSeconds.TryGetValue(seconds, out waitForSeconds))
{
_WaitForSeconds.Add(seconds, waitForSeconds = new WaitForSeconds(seconds));
}
return waitForSeconds;
}
}
WaitForEndOfFrame과 WaitForFixedUpdate의 경우, 변하지 않는 값이기 때문에 하나만 생성해두고 사용합니다. readOnly 키워드를 사용하면 변하지 않도록 필드를 선언할 수 있습니다.
public static readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame();
public static readonly WaitForFixedUpdate WaitForFixedUpdate = new WaitForFixedUpdate();
Comparer 클래스를 통해 박싱이 발생하지 않도록 해주고 의도치 않게 가비지가 생성되지 않도록 관리해줄 수 있습니다.
class Comparer : IEqualityComparer<float>
{
bool IEqualityComparer<float>.Equals(float x, float y)
{
return x == y;
}
int IEqualityComparer<float>.GetHashCode(float obj)
{
return obj.GetHashCode();
}
}
WaitForSeconds 메서드를 통해 딕셔너리에 저장된 값이 있는지 확인(TryGetValue)하여 있다면 그 값을 반환해주고 없다면 딕서녀리에 새로 추가하고 반환해줍니다.
public static WaitForSeconds WaitForSeconds(float seconds)
{
WaitForSeconds waitForSeconds;
if (!_WaitForSeconds.TryGetValue(seconds, out waitForSeconds))
{
_WaitForSeconds.Add(seconds, waitForSeconds = new WaitForSeconds(seconds));
}
return waitForSeconds;
}