Version: 2022.2
언어: 한국어
IL2CPP로 관리되는 스택 추적
관리되는 코드 스트리핑

스크립팅 제약

Unity는 지원하는 모든 플랫폼에 동일한 스크립팅 API와 경험을 제공합니다. 하지만 몇몇 플랫폼에는 각각 고유한 제약이 있습니다. 이러한 제약을 이해하도록 돕기 위해, 각각의 제약이 어떤 플랫폼과 스크립팅 백엔드에 적용되는지가 아래 표에 설명되어 있습니다.

플랫폼(스크립팅 백엔드) Ahead-of-time compile *Supports threads
Android(IL2CPP) 지원 지원
Android(Mono) 지원 안 함 지원
iOS(IL2CPP) 지원 지원
Standalone(IL2CPP) 지원 지원
Standalone(Mono) 지원 안 함 지원
유니버설 Windows 플랫폼(IL2CPP) 지원 지원
WebGL(IL2CPP) 지원 지원 안 함

AOT(Ahead-of-time compile) 컴파일

몇몇 플랫폼은 런타임 코드 생성을 지원하지 않습니다. 따라서 타겟 디바이스에서 JIT(just-in-time) 컴파일에 의존하는 일부 관리되는 코드는 작동하지 않습니다. 이 경우 관리되는 코드를 AOT(ahead-of-time) 방식으로 컴파일해야 합니다. 많은 경우 이러한 방식의 차이는 없지만, 몇몇 경우에서는 AOT 플랫폼을 사용할 때 몇 가지 고려해야 할 사항이 있습니다.

반사

반사는 AOT 플랫폼에서 지원됩니다.그러나 이 컴파일러가 반사를 통해 코드가 사용되었음을 추론할 수 없는 경우 런타임에 코드가 존재하지 않을 수 있습니다.자세한 내용은 [[wiki:ManagedCodeStripping|Managed Code Stripping]]을 참조하십시오.

System.Reflection.Emit

AOT 플랫폼은 System.Reflection.Emit 네임스페이스의 메서드 중 어떤 것도 구현할 수 없습니다.

직렬화

AOT 플랫폼은 반사를 사용하므로 직렬화와 역직렬화 문제가 생길 수 있습니다. 직렬화 또는 역직렬화의 일부인 반사를 통해서만 타입 또는 메서드가 사용되는 경우, AOT 컴파일러는 해당 타입이나 메서드가 필요로 하는 코드를 감지할 수 없습니다.

일반 타입 및 메서드

일반 타입 및 메서드의 경우, 일반 인스턴스마다 다른 코드가 필요할 수 있으므로 컴파일러는 어떤 일반 인스턴스가 사용되는지 결정해야 합니다.예를 들어, List<int>에 대한 코드는 List<double>에 대한 코드와는 다릅니다.그러나 IL2CPP는 레퍼런스 타입에 대한 사용 코드를 공유하므로, List<object>List<string>에 대해 동일한 코드가 사용됩니다.

다음과 같은 경우 IL2CPP가 컴파일 시간을 찾지 못한 일반 타입 및 메서드를 참조할 수 있습니다.

  1. 런타임에 새 일반 인스턴스 생성:Activator.CreateInstance(typeof(SomeGenericType<>).MakeGenericType(someType));
  2. 일반 인스턴스에서 정적 메서드 호출:typeof(SomeGenericType<>).MakeGenericType(someType)).GetMethod("AMethod").Invoke(null, null);
  3. 정적 일반 메서드 호출:typeof(SomeType).GetMethod("GenericMethod").MakeGenericMethod(someType).Invoke(null, null);
  4. 컴파일 시점에 유추할 수 없는 일반 가상 함수에 대한 일부 호출.
  5. 깊게 중첩된 일반 값 타입이 있는 호출.예: Struct<Struct<Struct<...<Struct<int>>>>.

이러한 경우를 지원하기 위해 IL2CPP는 모든 타입의 파라미터에서 작동하는 일반 코드를 생성합니다.그러나 이 코드는 해당 타입의 크기나 레퍼런스 타입인지, 값 유형인지에 대한 가정을 할 수 없기 때문에 속도가 느립니다.더 빠른 일반 메서드가 생성되도록 보장하려면 다음을 수행하십시오. * 일반 인수가 항상 레퍼런스 타입인 경우 where: class 제약을 추가합니다.그런 다음 IL2CPP는 성능 저하를 일으키지 않는 레퍼런스 타입 공유를 사용하여 폴백 메서드를 생성합니다. * 일반 인수가 항상 값 타입인 경우 where: struct 제약을 추가합니다.이렇게 하면 일부 최적화가 가능하지만 값 타입이 서로 다를 수 있으므로 코드가 여전히 느려질 수 있습니다. * UsedOnlyForAOTCodeGeneration이라는 메서드를 생성하고 IL2CPP가 생성할 일반 타입 및 메서드에 대한 레퍼런스를 추가합니다.이 메서드는 호출할 필요도 없고 아마도 호출해서는 안 됩니다.아래 예제는 GenericType<MyStruct>에 대한 특수화가 생성되도록 합니다.

public void UsedOnlyForAOTCodeGeneration()
{
    // Ensure that IL2CPP will create code for MyGenericStruct
    // using MyStruct as an argument.
    new GenericType<MyStruct>();

    // Ensure that IL2CPP will create code for SomeType.GenericMethod
    // using MyStruct as an argument.
    new SomeType().GenericMethod<MyStruct>();

    public void OnMessage<T>(T value) 
    {
        Debug.LogFormat("Message value:{0}", value);
    }

    // Include an exception so we can be sure to know if this
    // method is ever called.
    throw new InvalidOperationException(
        "This method is used for AOT code generation only." +
        "Do not call it at runtime.");
}

‘Faster (smaller) builds’ 설정이 활성화된 경우 완전히 공유 가능한 단일 버전의 일반 코드만 컴파일됩니다.이렇게 하면 생성되는 메서드 수가 줄어들어 컴파일 시간과 빌드 크기가 줄어들지만, 런타임 성능이 저하될 수 있습니다.

네이티브 코드에서 관리되는 메서드 호출

네이티브 코드에서 호출될 수 있도록 C 함수 포인터로 마셜링해야 하는 관리되는 메서드는 AOT 플랫폼에서 몇 가지 제약 사항이 적용됩니다.

  • 관리되는 메서드는 정적 메서드여야 합니다.
  • 관리되는 메서드에는 [MonoPInvokeCallback] 속성이 있어야 합니다.
  • 관리 메서드가 일반인 경우 지원해야 하는 일반 특수화를 지정하기 위해 [MonoPInvokeCallback(Type)] 오버로드를 사용해야 할 수 있습니다.그렇다면 타입은 올바른 수의 일반 인수를 가진 일반 인스턴스여야 합니다.아래와 같이 하나의 메서드에서 여러 개의 [MonoPInvokeCallback] 속성을 가질 수 있습니다.
// Generates reverse P/Invoke wrappers for NameOf<long> and NameOf<int>
// Note that the types are only used to indicate the generic arguments.
[MonoPInvokeCallback(typeof(Action<long>))]
[MonoPInvokeCallback(typeof(Action<int>))]
private static string NameOfT<T>(T item) 
{
    return typeof(T).Name;
}

스레드 없음

몇몇 플랫폼은 스레드를 지원하지 않으므로, System.Threading 네임스페이스를 사용하는 관리되는 코드는 런타임 도중 작동하지 않습니다. 또한, .NET 클래스 라이브러리의 일부분은 스레드에 암시적으로 의존합니다. 자주 사용되는 예제로는 System.Timers.Timer 클래스가 있는데, 이 클래스는 스레드 지원에 의존합니다.

예외 필터

IL2CPP는 예외 필터를 지원하지만 IL2CPP가 C++ 예외를 사용하여 관리되는 예외를 구현하기 때문에 실행 순서 필터 문과 캐치 블록은 다릅니다.필터가 필드에 대한 쓰기를 차단하지 않는 한, 이것은 눈에 띄지 않습니다.

MarshalAs 및 FieldOffset 속성

IL2CPP는 런타임 시 MarhsalAsFieldOffset 속성의 반영을 지원하지 않으며, 이러한 속성을 컴파일 시점에 지원합니다. 적절한 플랫폼 호출 마셜링을 위해 이러한 속성을 사용해야 합니다.

동적 키워드

IL2CPP는 C# dynamic 키워드를 지원하지 않습니다. 이 키워드는 IL2CPP의 경우 불가능한 JIT 컴파일을 요구합니다.

Marshal.Prelink

IL2CPP는 Marshal.Prelink 또는 Marshal.PrelinkAll API 메서드를 지원하지 않습니다.

System.Diagnostics.Process API

IL2CPP는 System.Diagnostics.Process API 메서드를 지원하지 않습니다.데스크톱 플랫폼에서 이 기능이 필요한 경우에는 모노 스크립팅 백엔드를 사용하십시오.

IL2CPP로 관리되는 스택 추적
관리되는 코드 스트리핑