iOS 및 Mac Catalyst의 Mono 인터프리터
iOS 또는 Mac Catalyst용 .NET 다중 플랫폼 앱 UI(.NET MAUI) 앱을 컴파일하면 컴파일러가 앱 코드를 MSIL(Microsoft Intermediate Language)로 바꿉니다. 시뮬레이터 또는 Mac Catalyst 앱에서 iOS 앱을 실행하는 경우 .NET CLR(공용 언어 런타임)은 JIT(Just in Time) 컴파일러를 사용하여 MSIL을 컴파일합니다. 런타임에 MSIL은 네이티브 코드로 컴파일되며 앱의 올바른 아키텍처에서 실행할 수 있습니다.
그러나 디바이스에서 동적으로 생성된 코드의 실행을 허용하지 않는 Apple에서 설정한 iOS에 대한 보안 제한이 있습니다. 마찬가지로 동적으로 생성된 코드의 실행은 시뮬레이터의 ARM64 아키텍처에서 실행되는 iOS 앱 및 ARM64 아키텍처에서 실행되는 Mac Catalyst 앱에서 허용되지 않습니다. 이 제한을 충족하기 위해 iOS 및 Mac Catalyst 앱은 AOT(Ahead of Time) 컴파일러를 사용하여 관리 코드를 컴파일합니다. Apple 디바이스 또는 네이티브 Mac Catalyst 이진 파일에 배포할 수 있는 네이티브 iOS 이진 파일을 생성합니다.
AOT는 시작 시간 단축 및 기타 다양한 성능 최적화를 통해 이점을 제공합니다. 그러나 앱에서 특정 기능이 사용되지 않도록 제한합니다.
- 제한된 제네릭 지원이 있습니다. 컴파일 시간에 가능한 모든 제네릭 인스턴스화를 확인할 수 있는 것은 아닙니다. .NET MAUI 릴리스 빌드에서 발생하는 많은 iOS 관련 문제는 이 제한 사항 때문입니다.
- 동적 코드 생성은 허용되지 않습니다. 즉
System.Relection.Emit
, 사용할 수 없고, 지원System.Runtime.Remoting
되지 않으며, C# 동적 형식의 일부 사용은 허용되지 않습니다.
AOT 제한이 발생하면 System.ExecutionEngineException
"aot 전용 모드에서 실행되는 동안 JIT 컴파일 메서드를 시도"라는 메시지와 함께 throw됩니다.
Mono 인터프리터는 플랫폼 제한을 준수하면서 이러한 제한을 극복합니다. AOT가 나머지를 컴파일하는 동안 런타임에 앱의 일부 부분을 해석할 수 있습니다. 그러나 프로덕션 앱에서 인터프리터를 사용하는 데 몇 가지 잠재적인 단점이 있습니다.
- 인터프리터를 사용하도록 설정하면 일반적으로 앱 크기가 크게 줄어들지만, 경우에 따라 앱 크기가 증가할 수 있습니다.
- 해석된 코드가 AOT 컴파일된 코드보다 더 느리게 실행되므로 앱 실행 속도가 느려집니다. 이 실행 속도 감소는 가급적에서 허용되지 않는 범위까지 다양할 수 있으므로 성능 테스트를 수행해야 합니다.
- 크래시 보고서의 네이티브 스택 추적은 실행 중인 코드를 언급하지 않는 인터프리터의 제네릭 프레임을 포함하므로 유용하지 않습니다. 그러나 관리되는 스택 추적은 변경되지 않습니다.
인터프리터는 기본적으로 .NET MAUI 디버그 빌드에 사용하도록 설정되며 릴리스 빌드에 사용하도록 설정할 수 있습니다.
팁
.NET MAUI iOS 앱 또는 ARM64 기반 Mac Catalyst 앱이 디버그 빌드로 올바르게 작동하지만 릴리스 빌드로 충돌하는 경우 앱의 릴리스 빌드에 인터프리터를 사용하도록 설정해 보세요. 앱 또는 해당 라이브러리 중 하나가 인터프리터가 필요한 기능을 사용하는 것일 수 있습니다.
인터프리터 사용
MsBuild 속성을 true
.NET MAUI 앱의 프로젝트 파일로 설정 $(UseInterpreter)
하여 iOS 릴리스 빌드에서 Mono 인터프리터를 사용하도록 설정할 수 있습니다.
<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
<UseInterpreter>true</UseInterpreter>
</PropertyGroup>
ARM64에서 Mac Catalyst 릴리스 빌드에 대해 인터프리터를 사용하도록 설정할 수도 있습니다.
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'maccatalyst-arm64' and '$(Configuration)' == 'Release'">
<UseInterpreter>true</UseInterpreter>
</PropertyGroup>
Warning
JIT 컴파일을 사용하지 않도록 설정하므로 Android에서 릴리스 빌드에 인터프리터를 사용하도록 설정하지 마세요.
iOS 및 Mac Catalyst에서는 MSBuild 속성을 사용하여 인터프리터를 $(MtouchInterpreter)
사용하도록 설정할 수도 있습니다. 이 속성은 필요에 따라 해석할 쉼표로 구분된 어셈블리 목록을 사용합니다. 또한 all
모든 어셈블리를 지정하는 데 사용할 수 있으며 접두사에 빼기 기호가 있으면 어셈블리가 AOT 컴파일됩니다. 다음을 수행할 수 있습니다.
- 를 지정하여 모든 어셈블리를
all
해석하거나 AOT를 지정하여 모든 항목을 컴파일합니다-all
. - -MyAssembly를 지정하여 MyAssembly 또는 AOT 컴파일 개별 어셈블리를 지정하여 개별 어셈블리를 해석합니다.
- 혼합하고 일치하여 일부 어셈블리를 해석하고 AOT는 다른 어셈블리를 컴파일합니다.
Warning
인터프리터는 네이티브 AOT 배포 $(UseInterpreter)
와 호환되지 않으므로 네이티브 AOT를 사용할 때 MSBuild 속성과 $(MtouchInterpreter)
MSBuild 속성은 영향을 주지 않습니다. 자세한 내용은 네이티브 AOT 배포를 참조하세요.
다음 예제에서는 System.Xml.dll 제외한 모든 어셈블리를 해석하는 방법을 보여줍니다.
<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
<!-- Interpret everything, except System.Xml.dll -->
<MtouchInterpreter>all,-System.Xml</MtouchInterpreter>
</PropertyGroup>
다음 예제에서는 AOT가 System.Numerics.dll 제외한 모든 어셈블리를 컴파일하는 방법을 보여줍니다.
<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
<!-- AOT everything, except System.Numerics.dll, which will be interpreted -->
<MtouchInterpreter>-all,System.Numerics</MtouchInterpreter>
</PropertyGroup>
Important
인터프리터가 실행하는 스택 프레임은 유용한 정보를 제공하지 않습니다. 그러나 인터프리터는 어셈블리별로 사용하지 않도록 설정할 수 있으므로 일부 어셈블리의 스택 프레임이 크래시 보고서에 정확하게 표시될 수 있습니다.
또는 인터프리터가 동적 코드 생성을 수행하도록 허용하면서 AOT가 모든 어셈블리를 컴파일하도록 다음 예제를 사용합니다.
<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
<MtouchInterpreter>-all</MtouchInterpreter>
</PropertyGroup>
인터프리터가 필요한 또 다른 일반적인 시나리오는 ARM64 아키텍처에서 실행되는 .NET MAUI Mac Catalyst 앱으로, 시작 시 예외를 throw할 수 있습니다. 인터프리터를 사용하도록 설정하여 이 시작 예외를 수정할 수 있는 경우가 많습니다.
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'maccatalyst-arm64' and '$(Configuration)' == 'Release'">
<MtouchInterpreter>-all,MyAssembly</MtouchInterpreter>
</PropertyGroup>
.NET MAUI