Xamarin.Mac のしくみ
ほとんどの場合、開発者は Xamarin.Mac の内部の "マジック" について心配する必要はありませんが、内部で物事がどのように機能するかを大まかに理解すると、C# レンズで既存のドキュメントを解釈し、発生した場合の問題のデバッグの両方に役立ちます。
Xamarin.Mac では、アプリケーションが Objective-C 2 つのワールドをブリッジします。ネイティブ クラスのインスタンス (、など) を含むベース ランタイムと、NSApplication
マネージド クラス (NSString
System.String
、HttpClient
など) のインスタンスを含む C# ランタイムがあります。 これら 2 つの世界の間で、Xamarin.Mac は双方向ブリッジを作成します。これにより、アプリは ( などNSApplication.Init
) でObjective-Cメソッド (セレクター) を呼び出し、アプリの C# メソッド (アプリ デリゲートのメソッドなど) Objective-C を呼び出すことができます。 一般に、 への 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 ()
{
}
}
この例では、Objective-Cランタイムは、 と run
というセレクターを使用して 呼び出された init
MyClass
クラスについて認識するようになりました。
ほとんどの場合、これは開発者が無視できる実装の詳細です。アプリが受け取るほとんどのコールバックは、クラス (AppDelegate
、Delegates
、DataSources
) でオーバーライドされたメソッドbase
、または API に渡されるアクションのいずれかによって行われます。 いずれの場合も、 Export
C# コードでは属性は必要ありません。
コンストラクターのランスルー
多くの場合、開発者はアプリの C# クラス構築 API をランタイムに公開して Objective-C 、ストーリーボードや XIB ファイルで呼び出されたときなどの場所からインスタンス化できるようにする必要があります。 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)
{
}
一般に、開発者は、カスタムNSViews
単独などの一部のIntPtr
型を作成するときに生成される および NSCoder
コンストラクターを残す必要があります。 Xamarin.Mac がランタイム要求に応答 Objective-C してこれらのコンストラクターのいずれかを呼び出す必要があり、それを削除した場合、アプリはネイティブ コード内でクラッシュし、問題を正確に把握することが困難な場合があります。
メモリ管理とサイクル
Xamarin.Mac でのメモリ管理は、Xamarin.iOS とよく似ています。 また、このドキュメントの範囲を超えた複雑なトピックでもあります。 メモリとパフォーマンスのベスト プラクティスをお読みください。
事前コンパイル
通常、.NET アプリケーションはビルド時にコンピューター コードにコンパイルされません。代わりに、アプリの起動時にコンピューター コードに コンパイルされる Just-In-Time (JIT) を取得する IL コードと呼ばれる中間層にコンパイルされます。
Mono ランタイムが JIT でこのコンピューター コードをコンパイルするのにかかる時間は、必要なマシン コードが生成されるまでに時間がかかるため、Xamarin.Mac アプリの起動が最大 20% 遅くなる可能性があります。
iOS で Apple によって課される制限により、IL コードの JIT コンパイルは Xamarin.iOS では使用できません。 その結果、すべての Xamarin.iOS アプリは、ビルド サイクル中にマシン コードにコンパイルされた完全な Ahead-Of-Time (AOT) になります。
Xamarin.Mac の新機能は、Xamarin.iOS と同様に、アプリのビルド サイクル中に IL コードを AOT する機能です。 Xamarin.Mac では、必要なコンピューター コードの大部分をコンパイルする ハイブリッド AOT アプローチを使用しますが、ランタイムは必要なトランポラインをコンパイルでき、Reflection.Emit (および現在 Xamarin.Mac で動作するその他のユース ケース) を引き続きサポートする柔軟性を備えています。
AOT が Xamarin.Mac アプリを支援できる主な領域は 2 つあります。
- より優れた "ネイティブ" クラッシュ ログ - ネイティブ コードで Xamarin.Mac アプリケーションがクラッシュした場合 (Cocoa API に無効な呼び出しを行うときに一般的に発生します (これを受け入れないメソッドに を
null
送信するなど)、JIT フレームを含むネイティブ クラッシュ ログは分析が困難です。 JIT フレームにはデバッグ情報がないため、16 進オフセットを持つ複数の行があり、何が起こっているのかを知らなくなります。 AOT では、"実際の" 名前付きフレームが生成され、トレースの読み取りがはるかに簡単になります。 これは、Xamarin.Mac アプリが lldb や Instruments などのネイティブ ツールとより適切に対話することを意味します。 - 起動時間のパフォーマンスの向上 - 大規模な Xamarin.Mac アプリケーションの場合、複数回の起動時間で、すべてのコードの JIT コンパイルにかなりの時間がかかる場合があります。 AOT では、これは前もって機能します。
AOT コンパイルの有効化
Xamarin.Mac で AOT が有効になっているのは、ソリューション エクスプローラーの [プロジェクト名] をダブルクリックし、[Mac ビルド] に移動し、[追加の mmp 引数: ] フィールドに追加--aot:[options]
します (ここで[options]
、AOT の種類を制御するための 1 つ以上のオプションは以下を参照)。 次に例を示します。
重要
AOT コンパイルを有効にすると、ビルド時間が大幅に増加し、場合によっては数分まで増加しますが、アプリの起動時間を平均で 20% 向上させることができます。 その結果、AOT コンパイルは Xamarin.Mac アプリの リリース ビルドでのみ有効にする必要があります。
Aot コンパイル オプション
Xamarin.Mac アプリで AOT コンパイルを有効にするときに調整できるさまざまなオプションがあります。
none
- AOT コンパイルなし。 これが既定の設定です。all
- AOT は MonoBundle 内のすべてのアセンブリをコンパイルします。core
- AOT は、、および アセンブリをXamarin.Mac
System
mscorlib
コンパイルします。sdk
- AOT は、 および 基本クラス ライブラリ (BCL) アセンブリをコンパイルXamarin.Mac
します。|hybrid
- 上記のいずれかのオプションにこれを追加すると、ハイブリッド AOT が有効になり、IL の削除が可能になりますが、コンパイル時間が長くなります。+
- AOT コンパイル用の 1 つのファイルが含まれます。-
- AOT コンパイルから 1 つのファイルを削除します。
たとえば、 --aot:all,-MyAssembly.dll
は MonoBundleMyAssembly.dll
--aot:core|hybrid,+MyOtherAssembly.dll,-mscorlib.dll
内のすべてのアセンブリで AOT コンパイルを有効にします。また、ハイブリッドを有効にします。コード AOT には、 と を除く がMyOtherAssembly.dll
mscorlib.dll
含まれます。
部分的な静的 registrar
Xamarin.Mac アプリを開発する場合、変更を完了してからテストまでの時間を最小限に抑えることは、開発期限を満たすことが重要になる可能性があります。 コードベースや単体テストのモジュール化などの戦略は、アプリがコストのかかる完全な再構築を必要とする回数を減らすので、コンパイル時間を短縮するのに役立ちます。
さらに、Xamarin.Mac の新機能である Partial Static Registrar (Xamarin.iOS によって開拓された) を使用すると、 デバッグ 構成での Xamarin.Mac アプリの起動時間を大幅に短縮できます。 部分静的 Registrar を使用すると、デバッグの起動でほぼ 5 倍の改善が実現する方法を理解すると、 の概要 registrar 、静的と動的の違い、およびこの "部分的な静的" バージョンの動作について少しの背景が得られます。
について registrar
Xamarin.Mac アプリケーションの内部には、Apple と Objective-C ランタイムの Cocoa フレームワークがあります。 この "ネイティブ世界" と C# の "マネージド ワールド" の間に橋渡しを構築することは、Xamarin.Mac の主な責任です。 このタスクの一部は、 メソッド内NSApplication.Init ()
で実行される によってregistrar処理されます。 これは、Xamarin.Mac での Cocoa API の使用が最初に呼び出される必要 NSApplication.Init
がある理由の 1 つです。
registrarのジョブは、 などのNSWindow
NSApplicationDelegate
NSView
クラスから派生したアプリの C# クラスの存在をランタイムに通知Objective-Cすることです。NSObject
これには、登録する必要がある内容とレポートする各型の要素を決定するために、アプリ内のすべての型のスキャンが必要です。
このスキャンは、リフレクションを使用してアプリケーションの起動時 に動的に実行することも、ビルド時のステップとして 静的に実行することもできます。 登録の種類を選択する場合、開発者は次の点に注意する必要があります。
- 静的登録を使用すると、起動時間を大幅に短縮できますが、ビルド時間が大幅に短縮される可能性があります (通常は 2 倍以上のデバッグ ビルド時間)。 これは、 リリース 構成ビルドの既定値になります。
- 動的登録は、アプリケーションの起動までこの作業を遅らせ、コード生成をスキップしますが、この追加作業により、アプリケーションの起動で顕著な一時停止 (少なくとも 2 秒) が発生する可能性があります。 これは、デバッグ構成ビルドで特に顕著です。これは、既定では動的登録に設定され、リフレクションが遅くなります。
Xamarin.iOS 8.13 で最初に導入された部分静的登録は、開発者に両方のオプションのベストを提供します。 内のすべての要素 Xamarin.Mac.dll
の登録情報を事前に計算し、静的ライブラリ内の Xamarin.Mac でこの情報を配布することで (ビルド時にのみ にリンクする必要があります)、Microsoft は動的 registrar なリフレクション時間の大部分を削除し、ビルド時間に影響を与えなくなりました。
部分静的の有効化 registrar
Xamarin.Mac で部分静的Registrarは、ソリューション エクスプローラーの [プロジェクト名] をダブルクリックし、[Mac ビルド] に移動し、[追加の mmp 引数:] フィールドに追加--registrar:static
することで有効になります。 次に例を示します。
その他のリソース
内部的な動作の詳細な説明を次に示します。