Unity는 지원되는 모든 플랫폼에서 공통 스크립팅 API와 환경을 제공합니다. 하지만 일부 플랫폼에는 고유한 제한 사항이 있습니다. 이러한 제한 사항을 이해할 수 있도록 다음 표에 각 플랫폼 및 스크립팅 백엔드에 적용되는 제한 사항을 정리했습니다.
| 플랫폼(스크립팅 백엔드) | Ahead-of-time 컴파일 | 스레드 지원 |
|---|---|---|
| Android(IL2CPP) | 지원 | 지원 |
| Android(Mono) | 지원 안 함 | 지원 |
| iOS(IL2CPP) | 지원 | 지원 |
| 스탠드얼론(IL2CPP) | 지원 | 지원 |
| 스탠드얼론(Mono) | 지원 안 함 | 지원 |
| 유니버설 Windows 플랫폼(IL2CPP) | 지원 | 지원 |
| 웹(IL2CPP) | 지원 | 지원 안 함 |
일부 플랫폼은 런타임 코드 생성을 허용하지 않습니다. 타겟 기기의 JIT(Just-In-Time) 컴파일에 의존하는 관리되는 코드는 모두 실패합니다. 그러므로 모든 관리되는 코드를 AOT(Ahead-of-Time) 컴파일해야 합니다. 이 차이가 중요하지 않은 경우가 많지만, 몇몇 특정한 경우에는 AOT 플랫폼을 추가로 고려해야 합니다.
Unity는 AOT 플랫폼에서 반사를 지원합니다. 하지만 이 컴파일러가 반사를 통해 코드가 사용되었음을 추론할 수 없는 경우 런타임 시 코드가 존재하지 않을 수 있습니다. 자세한 내용은 관리되는 코드 스트리핑을 참조하십시오.
AOT 플랫폼은 System.Reflection.Emit 네임스페이스의 메서드 중 어떤 것도 구현할 수 없습니다.
AOT 플랫폼은 반사를 사용하므로 직렬화와 역직렬화 문제가 발생할 수 있습니다. 직렬화 또는 역직렬화의 일부인 반사를 통해서만 유형 또는 메서드가 사용되는 경우, AOT 컴파일러는 해당 유형이나 메서드가 필요로 하는 코드를 감지할 수 없습니다.
일반 유형 및 메서드의 경우, 다른 일반 인스턴스에 다른 코드가 필요할 수 있으므로 컴파일러가 어떤 일반 인스턴스를 사용할지 결정해야 합니다. 예를 들어, List<int>의 코드는 List<double>의 코드와 다릅니다. 그러나 IL2CPP는 레퍼런스 유형의 용도에 대한 코드를 공유하므로 List<object> 및 List<string>에 동일한 코드가 사용됩니다.
다음과 같은 경우 IL2CPP가 컴파일 시간을 찾지 못한 일반 유형 및 메서드를 참조할 수 있습니다.
Activator.CreateInstance(typeof(SomeGenericType<>).MakeGenericType(someType));
typeof(SomeGenericType<>).MakeGenericType(someType)).GetMethod(“AMethod”).Invoke(null, null);
typeof(SomeType).GetMethod(“GenericMethod”).MakeGenericMethod(someType).Invoke(null, null);
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++ 예외를 사용하여 관리되는 예외를 구현하기 때문에 실행 순서 필터 문과 캐치 블록은 다릅니다. 필터가 필드에 대한 쓰기를 차단하지 않는 한 눈에 띄지 않습니다.
IL2CPP는 런타임 시점에 MarshalAs 및 FieldOffset 속성의 반사를 지원하지 않습니다. 이러한 속성은 컴파일 시 지원합니다. 적절한 플랫폼 호출 마셜링에 사용해야 합니다.
IL2CPP는 C# dynamic 키워드를 지원하지 않습니다. 이 키워드에는 JIT 컴파일이 필요하며, IL2CPP에서는 불가능합니다.
IL2CPP는 Marshal.Prelink 또는 Marshal.PrelinkAll API 메서드를 지원하지 않습니다.
IL2CPP는 System.Diagnostics.Process API 메서드를 지원하지 않습니다. 데스크톱 플랫폼에서 필요한 경우에는 Mono 스크립팅 백엔드를 사용하십시오.