iOS-App-Architektur

Xamarin.iOS-Anwendungen werden in der Mono-Ausführungsumgebung ausgeführt, und verwenden Sie die vollständige AOT-Kompilierung (Ahead of Time), um C#-Code in die ARM-Assemblysprache zu kompilieren. Dies wird parallel zur Objective-C Runtime ausgeführt. Beide Laufzeitumgebungen werden auf einem UNIX-ähnlichen Kernel ausgeführt, insbesondere XNU, und machen verschiedene APIs für den Benutzercode verfügbar, sodass Entwickler auf das zugrunde liegende systemeigene oder verwaltete System zugreifen können.

Das folgende Diagramm zeigt eine grundlegende Übersicht über diese Architektur:

This diagram shows a basic overview of the Ahead of Time (AOT) compilation architecture

Systemeigener und verwalteter Code: Eine Erläuterung

Bei der Entwicklung für Xamarin werden häufig systemeigene und verwaltete Code verwendet. Verwalteter Code ist Code , der von der .NET Framework Common Language Runtime verwaltet wird, oder in Xamarins Fall: der Mono-Runtime. Das nennen wir eine Zwischensprache.

Systemeigener Code ist Code, der nativ auf der spezifischen Plattform ausgeführt wird (z Objective-C . B. AOT kompilierter Code, auf einem ARM-Chip). In diesem Handbuch wird erläutert, wie AOT Ihren verwalteten Code in systemeigenem Code kompiliert, und erläutert, wie eine Xamarin.iOS-Anwendung funktioniert und die iOS-APIs von Apple vollständig über die Verwendung von Bindungen nutzt und gleichzeitig Zugriff auf . BCL von NET und eine anspruchsvolle Sprache wie C#.

AOT

Wenn Sie eine Xamarin-Plattformanwendung kompilieren, wird der Mono C#-Compiler (oder F#) ausgeführt und kompiliert Ihren C#- und F#-Code in Microsoft Intermediate Language (MSIL). Wenn Sie eine Xamarin.Android-Anwendung, eine Xamarin.Mac-Anwendung oder sogar eine Xamarin.iOS-Anwendung auf dem Simulator ausführen, kompiliert die .NET Common Language Runtime (CLR) die MSIL mit einem Just in Time(JIT)-Compiler. Zur Laufzeit wird dies in einem systemeigenen Code kompiliert, der auf der richtigen Architektur für Ihre Anwendung ausgeführt werden kann.

Es gibt jedoch eine von Apple festgelegte Sicherheitsbeschränkung für iOS, wodurch die Ausführung von dynamisch generiertem Code auf einem Gerät nicht zulässig ist. Um sicherzustellen, dass wir diese Sicherheitsprotokolle einhalten, verwendet Xamarin.iOS stattdessen einen Compiler (Ahead of Time, AOT), um den verwalteten Code zu kompilieren. Dies erzeugt eine systemeigene iOS-Binärdatei, optional optimiert mit LLVM für Geräte, die auf dem ARM-basierten Prozessor von Apple bereitgestellt werden können. Ein grobes Diagramm, wie dies zusammenpasst, ist unten dargestellt:

A rough diagram of how this fits together

Die Verwendung von AOT weist eine Reihe von Einschränkungen auf, die im Leitfaden zu Einschränkungen ausführlich beschrieben sind. Darüber hinaus bietet sie eine Reihe von Verbesserungen gegenüber JIT durch eine Reduzierung der Startzeit und verschiedene Leistungsoptimierungen.

Nachdem wir nun untersucht haben, wie der Code aus der Quelle in nativen Code kompiliert wird, werfen wir einen Blick unter die Haube, um zu sehen, wie Xamarin.iOS es uns ermöglicht, vollständig native iOS-Anwendungen zu schreiben.

Selektoren

Mit Xamarin haben wir zwei separate Ökosysteme, .NET und Apple, die wir zusammenbringen müssen, um so optimiert wie möglich zu erscheinen, um sicherzustellen, dass das Endziel eine reibungslose Benutzererfahrung ist. Wir haben im Abschnitt oben gesehen, wie die beiden Laufzeiten kommunizieren, und Sie haben möglicherweise sehr gut von dem Begriff "Bindungen" gehört, mit dem die nativen iOS-APIs in Xamarin verwendet werden können. Bindungen werden in unserer Objective-C Bindungsdokumentation ausführlich erläutert, also lassen Sie uns jetzt untersuchen, wie iOS unter der Haube funktioniert.

Zunächst muss es eine Möglichkeit geben, C# verfügbar zu Objective-C machen, die über Selektoren durchgeführt wird. Eine Auswahl ist eine Nachricht, die an ein Objekt oder eine Klasse gesendet wird. Dies Objective-C erfolgt über die objc_msgSend Funktionen. Weitere Informationen zur Verwendung von Selektoren finden Sie im Objective-C Leitfaden für Selektoren . Es muss auch eine Möglichkeit geben, verwalteten Code Objective-Cverfügbar zu machen, was aufgrund der Tatsache komplizierter ist, dass Objective-C nichts über den verwalteten Code weiß. Um dies zu umgehen, verwenden Registrarswir . Diese werden im nächsten Abschnitt ausführlicher erläutert.

Registrars

Wie oben Erwähnung, ist der Code, der registrar verwalteten Code Objective-Cverfügbar macht. Dazu wird eine Liste jeder verwalteten Klasse erstellt, die von NSObject abgeleitet wird:

  • Für alle Klassen, die keine vorhandene Objective-C Klasse umschließen, wird eine neue Objective-C Klasse mit Objective-C Mitgliedern erstellt, Spiegel alle verwalteten Member mit einem [Export]-Attribut enthalten.

  • In den Implementierungen für jedes Objective-C-Element wird Code automatisch hinzugefügt, um das Spiegel verwaltete Mitglied aufzurufen.

Der folgende Pseudocode zeigt ein Beispiel dafür, wie dies geschieht:

C# (verwalteter Code)

 class MyViewController : UIViewController{
     [Export ("myFunc")]
     public void MyFunc ()
     {
     }
 }

Objective-C:

@interface MyViewController : UIViewController { }

    -(void)myFunc;
@end

@implementation MyViewController {}

    -(void) myFunc
    {
        /* code to call the managed MyViewController.MyFunc method */
    }
@end

Der verwaltete Code kann die Attribute [Register][Export]und , die registrar verwendet werden, um zu wissen, dass das Objekt verfügbar Objective-Cgemacht werden muss. Das [Register] Attribut wird verwendet, um den Namen der generierten Objective-C Klasse anzugeben, falls der generierte Standardname nicht geeignet ist. Alle von NSObject abgeleiteten Klassen werden automatisch bei Objective-C. Das erforderliche [Export] Attribut enthält eine Zeichenfolge, bei der es sich um den in der generierten Objective-C Klasse verwendeten Selektor handelt.

Es gibt zwei Arten von registrars Verwendung in Xamarin.iOS – dynamisch und statisch:

  • Dynamisch – registrars Die Dynamische registrar führt die Registrierung aller Typen in Ihrer Assembly zur Laufzeit durch. Dazu werden Funktionen verwendet, die von Objective-Cder Laufzeit-API bereitgestellt werden. Die Dynamische registrar hat daher einen langsameren Start, aber eine schnellere Buildzeit. Dies ist der Standardwert für den iOS-Simulator. Systemeigene Funktionen (in der Regel in C), die als Trampoline bezeichnet werden, werden bei Verwendung der dynamischen registrarsAls Methodenimplementierungen verwendet. Sie variieren zwischen verschiedenen Architekturen.

  • Statisch registrars – Die statische registrar Generiert Objective-C Code während des Builds, der dann in eine statische Bibliothek kompiliert und mit der ausführbaren Datei verknüpft wird. Dies ermöglicht einen schnelleren Start, dauert aber während der Buildzeit länger. Dies wird standardmäßig für Gerätebuilds verwendet. Die Statische registrar kann auch mit dem iOS-Simulator verwendet werden, indem sie als mtouch Attribut in den Buildoptionen Ihres Projekts übergeben --registrar:static wird, wie unten dargestellt:

    Setting Additional mtouch arguments

Weitere Informationen zu den Besonderheiten des von Xamarin.iOS verwendeten iOS-Typregistrierungssystems finden Sie im Typhandbuch Registrar .

Anwendungsstart

Der Einstiegspunkt aller ausführbaren Xamarin.iOS-Dateien wird von einer Funktion bereitgestellt xamarin_main, die Mono initialisiert.

Je nach Projekttyp erfolgt Folgendes:

  • Für normale iOS- und tvOS-Anwendungen wird die verwaltete Main-Methode aufgerufen, die von der Xamarin-App bereitgestellt wird. Diese verwaltete Main-Methode ruft dann den UIApplication.MainEinstiegspunkt für Objective-C. UIApplication.Main ist die Bindung für Objective-Cdie Methode.UIApplicationMain
  • Für Erweiterungen wird die systemeigene Funktion – NSExtensionMain oder (NSExtensionmain für WatchOS-Erweiterungen) – von Apple-Bibliotheken bereitgestellt. Da es sich bei diesen Projekten um Klassenbibliotheken und nicht um ausführbare Projekte handelt, gibt es keine verwalteten Main-Methoden, die ausgeführt werden sollen.

All diese Startsequenz wird in einer statischen Bibliothek kompiliert, die dann mit Ihrer endgültigen ausführbaren Datei verknüpft ist, damit Ihre App weiß, wie sie sich vom Boden entfernt.

An diesem Punkt wurde unsere App gestartet, Mono wird ausgeführt, wir befinden uns in verwaltetem Code und wissen, wie systemeigener Code aufgerufen und zurück aufgerufen werden kann. Als Nächstes müssen wir mit dem Hinzufügen von Steuerelementen beginnen und die App interaktiv gestalten.

Generator

Xamarin.iOS enthält Definitionen für jede einzelne iOS-API. Sie können eines dieser Elemente im MaciOS-GitHub-Repository durchsuchen. Diese Definitionen enthalten Schnittstellen mit Attributen sowie alle erforderlichen Methoden und Eigenschaften. Beispielsweise wird der folgende Code verwendet, um eine UIToolbar im UIKit-Namespace zu definieren. Beachten Sie, dass es sich um eine Schnittstelle mit einer Reihe von Methoden und Eigenschaften handelt:

[BaseType (typeof (UIView))]
public interface UIToolbar : UIBarPositioning {
    [Export ("initWithFrame:")]
    IntPtr Constructor (CGRect frame);

    [Export ("barStyle")]
    UIBarStyle BarStyle { get; set; }

    [Export ("items", ArgumentSemantic.Copy)][NullAllowed]
    UIBarButtonItem [] Items { get; set; }

    [Export ("translucent", ArgumentSemantic.Assign)]
    bool Translucent { [Bind ("isTranslucent")] get; set; }

    // done manually so we can keep this "in sync" with 'Items' property
    //[Export ("setItems:animated:")][PostGet ("Items")]
    //void SetItems (UIBarButtonItem [] items, bool animated);

    [Since (5,0)]
    [Export ("setBackgroundImage:forToolbarPosition:barMetrics:")]
    [Appearance]
    void SetBackgroundImage ([NullAllowed] UIImage backgroundImage, UIToolbarPosition position, UIBarMetrics barMetrics);

    [Since (5,0)]
    [Export ("backgroundImageForToolbarPosition:barMetrics:")]
    [Appearance]
    UIImage GetBackgroundImage (UIToolbarPosition position, UIBarMetrics barMetrics);

    ...
}

Der in Xamarin.iOS aufgerufene btouch Generator verwendet diese Definitionsdateien und verwendet .NET-Tools, um sie in einer temporären Assembly zu kompilieren. Diese temporäre Assembly kann jedoch nicht zum Aufrufen von Objective-C Code verwendet werden. Der Generator liest dann die temporäre Assembly und generiert C#-Code, der zur Laufzeit verwendet werden kann. Wenn Sie ihrer Definition beispielsweise ein zufälliges Attribut .cs Datei hinzufügen, wird es nicht im ausgegebenen Code angezeigt. Der Generator weiß nicht darüber und weiß daher btouch nicht, ob er in der temporären Assembly gesucht wird, um ihn auszugeben.

Sobald die Xamarin.iOS.dll erstellt wurde, bündeln mtouch alle Komponenten zusammen.

Auf hoher Ebene wird dies erreicht, indem die folgenden Aufgaben ausgeführt werden:

  • Erstellen Sie eine App-Bündelstruktur.
  • Kopieren Sie ihre verwalteten Assemblys.
  • Wenn die Verknüpfung aktiviert ist, führen Sie den verwalteten Linker aus, um Ihre Assemblys zu optimieren, indem Sie nicht verwendete Teile ausschneiden.
  • AOT-Kompilierung.
  • Erstellen Sie eine systemeigene ausführbare Datei, die eine Reihe statischer Bibliotheken (eine für jede Assembly) ausgibt, die mit der nativen ausführbaren Datei verknüpft sind, sodass die systemeigene ausführbare Datei aus dem Startprogrammcode, dem registrar Code (falls statisch) und allen Ausgaben des AOT-Compilers besteht.

Ausführlichere Informationen zum Linker und zur Verwendung finden Sie im Linker-Handbuch .

Zusammenfassung

In diesem Leitfaden wurde die AOT-Kompilierung von Xamarin.iOS-Apps untersucht und Xamarin.iOS und seine Beziehung zu Objective-C tiefgehenden Informationen untersucht.