Funktionsweise von Xamarin.Mac

In den meisten Fällen wird sich der Entwickler keine Gedanken über die interne "Magie" von Xamarin.Mac machen müssen, aber ein grobes Verständnis davon, wie die Dinge unter der Haube funktionieren, hilft sowohl bei der Interpretation vorhandener Dokumentation mit einem C#-Objektiv als auch beim Debuggen von Problemen, wenn sie auftreten.

In Xamarin.Mac überbrückt eine Anwendung zwei Welten: Es gibt die basierte Runtime, die Objective-C Instanzen nativer Klassen (NSString, NSApplicationusw.) enthält, und es gibt die C#-Runtime, die Instanzen verwalteter Klassen enthält (System.String, HttpClientusw.). Zwischen diesen beiden Welten erstellt Xamarin.Mac eine bidirektionale Brücke, sodass eine App Methoden (Selektoren) in Objective-C aufrufen kann (z NSApplication.Init. B. ) und Objective-C die C#-Methoden der App zurückrufen kann (z. B. Methoden für einen App-Delegaten). Im Allgemeinen werden Aufrufe von Objective-C transparent über P/Invokes und einige Laufzeitcode, den Xamarin bereitstellt, verarbeitet.

Verfügbarmachen von C#-Klassen/-Methoden für Objective-C

Objective-C Damit die C#-Objekte einer App jedoch wieder aufgerufen werden können, müssen sie auf eine verständliche Weise Objective-C verfügbar gemacht werden. Dies erfolgt über die Register Attribute und Export . Betrachten Sie das folgende Beispiel:

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

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

In diesem Beispiel weiß die Objective-C Runtime nun über eine Klasse namens MyClass mit Selektoren namens init und run.

In den meisten Fällen ist dies ein Implementierungsdetail, das der Entwickler ignorieren kann, da die meisten Rückrufe, die eine App empfängt, entweder über überschriebene Methoden für base Klassen (z AppDelegate. B. , Delegates) DataSourcesoder über aktionen erfolgen, die an APIs übergeben werden. In all diesen Fällen Export sind Attribute im C#-Code nicht erforderlich.

Konstruktorausführung

In vielen Fällen muss der Entwickler die C#-Klassen-Konstruktions-API der App für die Objective-C Runtime verfügbar machen, damit sie von Orten instanziiert werden kann, z. B. wenn sie in Storyboard- oder XIB-Dateien aufgerufen wird. Hier sind die fünf gängigsten Konstruktoren aufgeführt, die in Xamarin.Mac-Apps verwendet werden:

// 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)
{
}

Im Allgemeinen sollte der Entwickler die Konstruktoren und NSCoder belassen, die IntPtr generiert werden, wenn einige Typen erstellt werden, z. B. benutzerdefiniertNSViews. Wenn Xamarin.Mac als Reaktion auf eine Objective-C Laufzeitanforderung einen dieser Konstruktoren aufrufen muss und Sie ihn entfernt haben, stürzt die App im nativen Code ab, und es kann schwierig sein, das Problem genau zu ermitteln.

Speicherverwaltung und Zyklen

Die Speicherverwaltung in Xamarin.Mac ähnelt in vielerlei Hinsicht Xamarin.iOS. Außerdem handelt es sich um ein komplexes Thema, das über den Rahmen dieses Dokuments hinausgeht. Lesen Sie die bewährten Methoden für Arbeitsspeicher und Leistung.

Vorabkompilierung

In der Regel werden .NET-Anwendungen beim Erstellen nicht in Computercode kompiliert, sondern in eine Zwischenebene namens IL-Code, die just-in-Time (JIT) in Computercode kompiliert wird, wenn die App gestartet wird.

Die Zeit, die die Mono-Runtime benötigt, um diesen Computercode zu kompilieren, kann den Start einer Xamarin.Mac-App um bis zu 20 % verlangsamen, da es Zeit dauert, bis der erforderliche Computercode generiert wird.

Aufgrund der von Apple auf iOS auferlegten Einschränkungen ist die JIT-Kompilierung des IL-Codes für Xamarin.iOS nicht verfügbar. Daher sind alle Xamarin.iOS-Apps im Voraus (AOT) vollständig, die während des Buildzyklus in Computercode kompiliert werden.

Neu bei Xamarin.Mac ist die Möglichkeit, den IL-Code während des App-Buildzyklus zu AOT zu verwenden, genau wie Xamarin.iOS. Xamarin.Mac verwendet einen Hybrid-AOT-Ansatz, der einen Großteil des benötigten Computercodes kompiliert, aber der Runtime das Kompilieren der benötigten Trampoline und die Flexibilität ermöglicht, Reflection.Emit weiterhin zu unterstützen (und andere Anwendungsfälle, die derzeit auf Xamarin.Mac funktionieren).

Es gibt zwei Hauptbereiche, in denen AOT eine Xamarin.Mac-App unterstützen kann:

  • Bessere "native" Absturzprotokolle : Wenn eine Xamarin.Mac-Anwendung im nativen Code abstürzt, was häufig vorkommt, wenn ungültige Aufrufe an Cocoa-APIs ausgeführt werden (z. B. das Senden von an null eine Methode, die dies nicht akzeptiert), sind native Absturzprotokolle mit JIT-Frames schwierig zu analysieren. Da die JIT-Frames keine Debuginformationen enthalten, gibt es mehrere Zeilen mit Hexadezimalversatz und keinen Hinweis darauf, was passiert ist. AOT generiert "echte" benannte Frames, und die Ablaufverfolgungen sind viel einfacher zu lesen. Dies bedeutet auch, dass die Xamarin.Mac-App besser mit nativen Tools wie lldb und Instruments interagiert.
  • Bessere Startzeitleistung : Bei großen Xamarin.Mac-Anwendungen mit einer Startzeit von mehreren Sekunden kann die JIT-Kompilierung des gesamten Codes viel Zeit in Anspruch nehmen. AOT erledigt dies im Voraus.

Aktivieren der AOT-Kompilierung

AOT wird in Xamarin.Mac aktiviert, indem Sie im Projektmappen-Explorer auf den Projektnamen doppelklicken, zu Mac Build navigieren und zum Feld Zusätzliche mmp-Argumente: hinzufügen --aot:[options] (wobei [options] eine oder mehrere Optionen zum Steuern des AOT-Typs ist, siehe unten). Beispiel:

Hinzufügen von AOT zu zusätzlichen mmp-Argumenten

Wichtig

Das Aktivieren der AOT-Kompilierung erhöht die Buildzeit erheblich, manchmal bis zu mehreren Minuten, aber die App-Startzeiten können um durchschnittlich 20 % verbessert werden. Daher sollte die AOT-Kompilierung nur für Releasebuilds einer Xamarin.Mac-App aktiviert werden.

Aot-Kompilierungsoptionen

Es gibt verschiedene Optionen, die beim Aktivieren der AOT-Kompilierung für eine Xamarin.Mac-App angepasst werden können:

  • none – Keine AOT-Kompilierung. Dies ist die Standardeinstellung.
  • all – AOT kompiliert jede Assembly im MonoBundle.
  • core – AOT kompiliert die Xamarin.MacAssemblys , System und mscorlib .
  • sdk : AOT kompiliert die Xamarin.Mac Assemblys und BCL-Assemblys (Base Class Libraries).
  • |hybrid – Wenn Sie dies zu einer der oben genannten Optionen hinzufügen, wird hybrides AOT aktiviert, was das Entfernen von IL ermöglicht, aber zu längeren Kompilierzeiten führt.
  • + – Enthält eine einzelne Datei für die AOT-Kompilierung.
  • - : Entfernt eine einzelne Datei aus der AOT-Kompilierung.

Würde z. --aot:all,-MyAssembly.dll B. die AOT-Kompilierung für alle Assemblys im MonoBundle aktivieren, außerMyAssembly.dll und --aot:core|hybrid,+MyOtherAssembly.dll,-mscorlib.dll würde hybrid aktivieren, Code-AOT einschließen und MyOtherAssembly.dll ausschließen.mscorlib.dll

Teilweise statisch registrar

Beim Entwickeln einer Xamarin.Mac-App kann die Minimierung der Zeit zwischen dem Abschließen einer Änderung und dem Testen der App wichtig sein, um die Entwicklungsfristen einzuhalten. Strategien wie die Modularisierung von Codebasiss und Komponententests können dazu beitragen, die Kompilierzeiten zu verkürzen, da sie die Anzahl der Male verringern, mit denen eine App eine teure vollständige Neuerstellung erfordert.

Darüber hinaus kann Partial Static Registrar (wie von Xamarin.iOS) neu in Xamarin.Mac die Startzeiten einer Xamarin.Mac-App in der Debugkonfiguration erheblich reduzieren. Wenn Sie verstehen, wie die Verwendung von Partial Static Registrar eine fast 5-fache Verbesserung beim Debugstart herauspressen kann, wird ein wenig Hintergrundinformationen dazu erhalten, was ist registrar , was der Unterschied zwischen statisch und dynamisch ist und was diese "partielle statische" Version bewirkt.

Informationen zum registrar

Unter der Haube jeder Xamarin.Mac-Anwendung liegt das Cocoa-Framework von Apple und die Objective-C Runtime. Der Bau einer Brücke zwischen dieser "nativen Welt" und der "verwalteten Welt" von C# liegt in der Hauptverantwortung von Xamarin.Mac. Ein Teil dieser Aufgabe wird von behandelt, das innerhalb NSApplication.Init () der registrar-Methode ausgeführt wird. Dies ist ein Grund dafür, dass jede Verwendung von Cocoa-APIs in Xamarin.Mac zuerst aufgerufen werden muss NSApplication.Init .

Die registrarAufgabe der App besteht darin, die Objective-C Laufzeit über das Vorhandensein der C#-Klassen der App zu informieren, die von Klassen wie NSApplicationDelegate, , NSViewNSWindowund NSObjectabgeleitet werden. Dies erfordert eine Überprüfung aller Typen in der App, um zu bestimmen, welche Elemente für jeden Typ registriert werden müssen.

Diese Überprüfung kann entweder dynamisch, beim Start der Anwendung mit Reflektion oder statisch als Buildzeitschritt durchgeführt werden. Bei der Auswahl eines Registrierungstyps sollte der Entwickler Folgendes beachten:

  • Die statische Registrierung kann die Startzeiten drastisch reduzieren, aber die Buildzeiten erheblich verlangsamen (in der Regel mehr als die doppelte Debugbuildzeit). Dies ist die Standardeinstellung für Releasekonfigurationsbuilds.
  • Die dynamische Registrierung verzögert diese Arbeit bis zum Anwendungsstart und überspringt die Codegenerierung. Diese zusätzliche Arbeit kann jedoch zu einer spürbaren Pause (mindestens zwei Sekunden) beim Anwendungsstart führen. Dies ist besonders bei Debugkonfigurationsbuilds spürbar, die standardmäßig auf dynamische Registrierungen festgelegt sind und deren Reflektion langsamer erfolgt.

Die partielle statische Registrierung, die erstmals in Xamarin.iOS 8.13 eingeführt wurde, bietet entwicklern das Beste aus beiden Optionen. Durch das Vorabcomputing der Registrierungsinformationen jedes Elements in Xamarin.Mac.dll und das Versenden dieser Informationen mit Xamarin.Mac in einer statischen Bibliothek (die nur zur Buildzeit verknüpft werden muss), hat Microsoft den Größten Teil der Reflexionszeit der dynamischen registrar Entfernt, ohne die Buildzeit zu beeinträchtigen.

Aktivieren der partiellen statischen registrar

Partial Static Registrar wird in Xamarin.Mac aktiviert, indem Sie im Projektmappen-Explorer auf den Projektnamen doppelklicken, zu Mac-Build navigieren und --registrar:static zum Feld Zusätzliche mmp-Argumente hinzufügen. Beispiel:

Hinzufügen der partiellen statischen registrar zu zusätzlichen mmp-Argumenten

Zusätzliche Ressourcen

Im Folgenden finden Sie ausführlichere Erläuterungen zur internen Funktionsweise: