Xamarin.Mac 작동 방법

대부분의 경우 개발자는 Xamarin.Mac의 내부 "매직"에 대해 걱정할 필요가 없습니다. 그러나 내부적으로 작동 방식을 대략적으로 이해하면 C# 렌즈를 사용하여 기존 설명서를 해석하고 문제가 발생할 때 디버깅하는 데 도움이 됩니다.

Xamarin.Mac에서 애플리케이션은 Objective-C 두 가지 세계를 연결합니다. 네이티브 클래스(NSStringNSApplication등)의 인스턴스를 포함하는 기반 런타임이 있으며 관리되는 클래스(System.StringHttpClient등)의 인스턴스를 포함하는 C# 런타임이 있습니다. 이러한 두 세계 사이에 Xamarin.Mac은 앱이 (예: ) Objective-C 메서드(예: )를 호출하고 Objective-C 앱의 C# 메서드(예: NSApplication.Init앱 대리자의 메서드)를 다시 호출할 수 있도록 양방향 브리지를 만듭니다. 일반적으로 호출 Objective-C 은 P/Invokes 및 Xamarin에서 제공하는 일부 런타임 코드를 통해 투명하게 처리됩니다.

C# 클래스/메서드를 에 노출 Objective-C

그러나 Objective-C 앱의 C# 개체를 다시 호출하려면 이해할 수 있는 방식으로 Objective-C 노출되어야 합니다. 이 작업은 및 Export 특성을 통해 Register 수행됩니다. 다음 예제를 참조하세요.

[Register ("MyClass")]
public class MyClass : NSObject
{
   [Export ("init")]
   public MyClass ()
   {
   }

   [Export ("run")]
   public void Run ()
   {
   }
}

이 예제에서 런타임은 이제 호출 init 된 선택기와 함께 호출 MyClass 된 클래스에 대해 알게 됩니다run.Objective-C

대부분의 경우 앱이 수신하는 대부분의 콜백은 클래스(예AppDelegate: , DataSourcesDelegates) 또는 API에 전달된 작업에서 base 재정의된 메서드를 통해 수행되므로 개발자가 무시할 수 있는 구현 세부 정보입니다. 이러한 모든 경우에서는 Export C# 코드에서 특성이 필요하지 않습니다.

생성자 런스루

대부분의 경우 개발자는 스토리보드 또는 XIB 파일에서 호출될 때와 같은 위치에서 인스턴스화할 수 있도록 앱의 C# 클래스 생성 API Objective-C 를 런타임에 노출해야 합니다. 다음은 Xamarin.Mac 앱에서 사용되는 가장 일반적인 5가지 생성자입니다.

// Called when created from unmanaged code
public CustomView (IntPtr handle) : base (handle)
{
   Initialize ();
}

// Called when created directly from a XIB file
[Export ("initWithCoder:")]
public CustomView (NSCoder coder) : base (coder)
{
   Initialize ();
}

// Called from C# to instance NSView with a Frame (initWithFrame)
public CustomView (CGRect frame) : base (frame)
{
}

// Called from C# to instance NSView without setting the frame (init)
public CustomView () : base ()
{
}

// This is a special case constructor that you call on a derived class when the derived called has an [Export] constructor.
// For example, if you call init on NSString then you don’t want to call init on NSObject.
public CustomView () : base (NSObjectFlag.Empty)
{
}

일반적으로 개발자는 사용자 지정과 NSCoder 같은 일부 형식을 IntPtr 만들 때 생성되는 생성자와 생성 NSViews 자를 그대로 두어야 합니다. Xamarin.Mac이 런타임 요청에 대한 응답으로 Objective-C 이러한 생성자 중 하나를 호출해야 하는 경우 앱이 네이티브 코드 내에서 충돌하고 정확히 문제를 파악하기 어려울 수 있습니다.

메모리 관리 및 주기

Xamarin.Mac의 메모리 관리는 여러 가지 면에서 Xamarin.iOS와 매우 유사합니다. 또한 이 문서의 범위를 벗어나는 복잡한 항목이기도 합니다. 메모리 및 성능 모범 사례를 읽어 보세요.

미리 컴파일

일반적으로 .NET 애플리케이션은 빌드될 때 머신 코드로 컴파일되지 않으며, 대신 앱이 시작될 때 컴퓨터 코드로 컴파일된 JIT(Just-In-Time)를 가져오는 IL 코드라는 중간 계층으로 컴파일됩니다.

모노 런타임이 이 컴퓨터 코드를 컴파일하는 데 걸리는 시간은 필요한 컴퓨터 코드를 생성하는 데 시간이 걸리기 때문에 Xamarin.Mac 앱의 시작 속도를 최대 20%까지 늦출 수 있습니다.

iOS에서 Apple이 적용한 제한 사항으로 인해 Xamarin.iOS에서는 IL 코드의 JIT 컴파일을 사용할 수 없습니다. 따라서 모든 Xamarin.iOS 앱은 빌드 주기 동안 컴퓨터 코드로 컴파일된 전체 AOT(Ahead-Of-Time )가 됩니다.

Xamarin.Mac의 새로운 기능은 Xamarin.iOS가 할 수 있는 것처럼 앱 빌드 주기 동안 IL 코드를 AOT하는 기능입니다. Xamarin.Mac은 필요한 대부분의 컴퓨터 코드를 컴파일하는 하이브리드 AOT 접근 방식을 사용하지만 런타임에서 필요한 트램폴린을 컴파일하고 Reflection.Emit(및 현재 Xamarin.Mac에서 작동하는 기타 사용 사례)를 계속 지원할 수 있는 유연성을 허용합니다.

AOT가 Xamarin.Mac 앱을 도울 수 있는 두 가지 주요 영역이 있습니다.

  • 더 나은 "네이티브" 크래시 로그 - Xamarin.Mac 애플리케이션이 네이티브 코드에서 충돌하는 경우( Cocoa API를 수락하지 않는 메서드로 보내는 null 것과 같이) Cocoa API를 잘못 호출할 때 일반적으로 발생하는 경우 JIT 프레임이 있는 네이티브 크래시 로그는 분석하기 어렵습니다. JIT 프레임에는 디버그 정보가 없으므로 16진수 오프셋이 있는 여러 줄이 있고 무슨 일이 있었는지 알 수 없습니다. AOT는 명명된 "실제" 프레임을 생성하며 추적을 훨씬 쉽게 읽을 수 있습니다. 이는 또한 Xamarin.Mac 앱이 lldb 및 Instruments와 같은 네이티브 도구와 더 잘 상호 작용한다는 것을 의미합니다.
  • 더 나은 시작 시간 성능 - 큰 Xamarin.Mac 애플리케이션의 경우 두 번째 시작 시간이 여러 번 있는 경우 모든 코드를 컴파일하는 JIT에는 상당한 시간이 걸릴 수 있습니다. AOT는 이 작업을 미리 수행합니다.

AOT 컴파일 사용

AOT는 솔루션 탐색기 프로젝트 이름을 두 번 클릭하고 Mac Build로 이동하고 추가 mmp 인수에 추가하여 --aot:[options] Xamarin.Mac에서 사용하도록 설정됩니다. [options] 여기서 AOT 형식을 제어하는 하나 이상의 옵션은 아래 참조). 예시:

Adding AOT to additional mmp arguments

Important

AOT 컴파일을 사용하도록 설정하면 빌드 시간이 최대 몇 분까지 크게 증가하지만 앱 시작 시간은 평균 20%까지 향상될 수 있습니다. 따라서 AOT 컴파일은 Xamarin.Mac 앱의 릴리스 빌드에서만 사용하도록 설정해야 합니다.

Aot 컴파일 옵션

Xamarin.Mac 앱에서 AOT 컴파일을 사용하도록 설정할 때 조정할 수 있는 여러 가지 옵션이 있습니다.

  • none - AOT 컴파일이 없습니다. 이것이 기본 설정입니다.
  • all - AOT는 MonoBundle의 모든 어셈블리를 컴파일합니다.
  • core- AOT는 및 Systemmscorlib 어셈블리를 Xamarin.Mac컴파일합니다.
  • sdk - AOT는 Xamarin.Mac BCL(기본 클래스 라이브러리) 어셈블리를 컴파일합니다.
  • |hybrid - 위의 옵션 중 하나에 이 옵션을 추가하면 IL 제거를 허용하는 하이브리드 AOT를 사용할 수 있지만 컴파일 시간이 길어질 수 있습니다.
  • + - AOT 컴파일을 위한 단일 파일을 포함합니다.
  • - - AOT 컴파일에서 단일 파일을 제거합니다.

예를 들어 --aot:all,-MyAssembly.dll 하이브리드를 제외하고MyAssembly.dll--aot:core|hybrid,+MyOtherAssembly.dll,-mscorlib.dll MonoBundle의 모든 어셈블리에서 AOT 컴파일을 사용하도록 설정하고, 코드 AOT에는 다음을 포함하고 MyOtherAssembly.dll 제외합니다.mscorlib.dll

부분 정적 registrar

Xamarin.Mac 앱을 개발할 때 변경 완료와 테스트 사이의 시간을 최소화하면 개발 기한을 충족하는 것이 중요해질 수 있습니다. 코드베이스의 모듈화 및 단위 테스트와 같은 전략은 앱에 비용이 많이 드는 전체 다시 빌드가 필요한 횟수를 줄여 컴파일 시간을 줄이는 데 도움이 될 수 있습니다.

또한 Xamarin.Mac의 새로운 부분 정적Registrar(Xamarin.iOS에서 개척)을 사용하면 디버그 구성에서 Xamarin.Mac 앱의 시작 시간을 크게 줄일 수 있습니다. 부분 정적 Registrar 을 사용하여 디버그 시작에서 거의 5배 향상된 기능을 압착하는 방법을 이해하면 정의, registrar 정적 및 동적 간의 차이점, 이 "부분 정적" 버전이 수행하는 기능에 대한 약간의 배경 지식이 필요합니다.

정보 registrar

Xamarin.Mac 애플리케이션의 내부적으로 Apple의 Cocoa 프레임워크와 런타임이 Objective-C 있습니다. 이 "네이티브 월드"와 C#의 "관리되는 세계" 사이에 다리를 놓는 것은 Xamarin.Mac의 주요 책임입니다. 이 작업의 일부는 메서드 내에서 NSApplication.Init () 실행되는 에 의해 registrar처리됩니다. 이는 Xamarin.Mac에서 Cocoa API를 사용하는 경우 먼저 호출해야 하는 NSApplication.Init 이유 중 하나입니다.

registrar'의 작업은 런타임에 Objective-C , , NSWindowNSObject와 같은 NSApplicationDelegateNSView클래스에서 파생되는 앱의 C# 클래스가 있음을 알리는 것입니다. 이렇게 하려면 등록해야 하는 요소와 보고할 각 형식의 요소를 확인하기 위해 앱의 모든 형식을 검색해야 합니다.

이 검사는 리플렉션을 사용하여 애플리케이션을 시작할 때 동적으로 수행하거나 빌드 시간 단계로 정적으로 수행할 수 있습니다. 등록 유형을 선택할 때 개발자는 다음 사항을 알고 있어야 합니다.

  • 정적 등록은 시작 시간을 크게 줄일 수 있지만 빌드 시간이 크게 느려질 수 있습니다(일반적으로 디버그 빌드 시간이 두 배 이상). 릴리스 구성 빌드의 기본값입니다.
  • 동적 등록은 애플리케이션이 시작될 때까지 이 작업을 지연시키고 코드 생성을 건너뛰지만, 이 추가 작업은 애플리케이션 시작 시 눈에 띄는 일시 중지(최소 2초)를 만들 수 있습니다. 이는 디버그 구성 빌드에서 특히 두드러지며, 기본값은 동적 등록이고 리플렉션은 더 느립니다.

Xamarin.iOS 8.13에서 처음 도입된 부분 정적 등록은 개발자에게 두 옵션 중에서 가장 좋은 옵션을 제공합니다. 모든 요소 Xamarin.Mac.dll 의 등록 정보를 미리 계산하고 정적 라이브러리에서 Xamarin.Mac으로 이 정보를 전달함으로써(빌드 시만 연결해야 하는) Microsoft는 빌드 시간에 영향을 주지 않으면서 동적 registrar 의 리플렉션 시간의 대부분을 제거했습니다.

부분 정적 사용 registrar

부분 정적 Registrar 은 솔루션 탐색기 프로젝트 이름을 두 번 클릭하고 Mac Build로 이동하여 추가 mmp 인수인 필드에 추가하여 --registrar:staticXamarin.Mac에서 활성화됩니다. 예시:

Adding the partial static registrar to additional mmp arguments

추가 리소스

내부적으로 작업하는 방법에 대한 자세한 설명은 다음과 같습니다.