Typ registrar für Xamarin.iOS

In diesem Dokument wird das Typregistrierungssystem beschrieben, das von Xamarin.iOS verwendet wird.

Registrierung verwalteter Klassen und Methoden

Während des Startvorgangs wird Xamarin.iOS registriert:

  • Klassen mit einem [Register] -Attribut als Objective-C Klassen.
  • Klassen mit einem [Category]- Attribut als Objective-C Kategorien.
  • Schnittstellen mit einem [Protokoll]- Attribut als Objective-C Protokolle.
  • Member mit einem [Export]-Wert, der den Zugriff auf sie ermöglicht Objective-C .

Betrachten Sie beispielsweise die verwaltete Main Methode, die in Xamarin.iOS-Anwendungen üblich ist:

UIApplication.Main (args, null, "AppDelegate");

Dieser Code weist die Objective-C Runtime an, den Typ als AppDelegate Delegatenklasse der Anwendung zu verwenden. Damit die Objective-C Runtime eine instance der C#- AppDelegate Klasse erstellen kann, muss diese Klasse registriert sein.

Xamarin.iOS führt die Registrierung automatisch durch, entweder zur Laufzeit (dynamische Registrierung) oder zur Kompilierzeit (statische Registrierung).

Die dynamische Registrierung verwendet reflektion beim Start, um alle zu registrierenden Klassen und Methoden zu finden und an die Objective-C Runtime zu übergeben. Die dynamische Registrierung wird standardmäßig für Simulatorbuilds verwendet.

Die statische Registrierung überprüft zur Kompilierzeit die von der Anwendung verwendeten Assemblys. Es bestimmt die Klassen und Methoden, bei Objective-C denen sie registriert werden sollen, und generiert eine Zuordnung, die in Ihre Binärdatei eingebettet ist. Beim Start registriert sie dann die Zuordnung bei der Objective-C Runtime. Die statische Registrierung wird für Gerätebuilds verwendet.

Kategorien

Ab Xamarin.iOS 8.10 ist es möglich, Kategorien mit der C#-Syntax zu erstellen Objective-C .

Um eine Kategorie zu erstellen, verwenden Sie das [Category] Attribut, und geben Sie den zu erweiternden Typ an. Der folgende Code erweitert z. B. NSString:

[Category (typeof (NSString))]

Jede Der Methoden einer Kategorie verfügt über ein [Export] Attribut, das es für die Objective-C Runtime verfügbar macht:

[Export ("today")]
public static string Today ()
{
    return "Today";
}

Alle verwalteten Erweiterungsmethoden müssen statisch sein, aber es ist möglich, instance Methoden mithilfe der C#-Standardsyntax für Erweiterungsmethoden zu erstellen Objective-C :

[Export ("toUpper")]
public static string ToUpper (this NSString self)
{
    return self.ToString ().ToUpper ();
}

Das erste Argument für die Erweiterungsmethode ist das instance, für das die Methode aufgerufen wurde:

[Category (typeof (NSString))]
public static class MyStringCategory
{
    [Export ("toUpper")]
    static string ToUpper (this NSString self)
    {
        return self.ToString ().ToUpper ();
    }
 }

In diesem Beispiel wird der NSString -Klasse eine native toUpper instance-Methode hinzugefügt. Diese Methode kann von Objective-Caufgerufen werden:

[Category (typeof (UIViewController))]
public static class MyViewControllerCategory
{
    [Export ("shouldAutoRotate")]
    static bool GlobalRotate ()
    {
        return true;
    }
}

Protokolle

Ab Xamarin.iOS 8.10 werden Schnittstellen mit dem [Protocol] Attribut als Protokolle exportiert Objective-C :

[Protocol ("MyProtocol")]
interface IMyProtocol
{
    [Export ("method")]
    void Method ();
}

class MyClass : IMyProtocol
{
    void Method ()
    {
    }
}

Dieser Code exportiert IMyProtocol in Objective-C ein Protokoll namens MyProtocol und eine Klasse namens MyClass , die das Protokoll implementiert.

Neues Registrierungssystem

Ab der stabilen Version 6.2.6 und der Betaversion 6.3.4 haben wir eine neue statische registrarhinzugefügt. In der Version 7.2.1 haben wir den neuen registrar Standard festgelegt.

Dieses neue Registrierungssystem bietet die folgenden neuen Features:

  • Kompilierungszeiterkennung von Programmiererfehlern:

    • Zwei Klassen, die mit demselben Namen registriert werden.
    • Mehr als eine Methode exportiert, um auf denselben Selektor zu reagieren
  • Entfernen von nicht verwendetem nativem Code:

    • Das neue Registrierungssystem fügt starke Verweise auf Code hinzu, der in statischen Bibliotheken verwendet wird, sodass der native Linker nicht verwendeten nativen Code aus der resultierenden Binärdatei entfernen kann. Bei den Xamarin-Beispielbindungen werden die meisten Anwendungen mindestens 300.000 kleiner.
  • Unterstützung für generische Unterklassen von NSObject. Weitere Informationen finden Sie unter NSObject Generics . Darüber hinaus fängt das neue Registrierungssystem nicht unterstützte generische Konstrukte ab, die zuvor zufälliges Verhalten zur Laufzeit verursacht hätten.

Fehler, die vom neuen erfasst werden registrar

Im Folgenden finden Sie einige Beispiele für die Fehler, die vom neuen registrarabgefangen werden.

  • Exportieren desselben Selektors mehrmals in derselben Klasse:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo:")]
        void Foo (NSString str);
        [Export ("foo:")]
        void Foo (string str)
    }
    
  • Exportieren mehrerer verwalteter Klassen mit demselben Objective-C Namen:

    [Register ("Class")]
    class MyClass : NSObject {}
    
    [Register ("Class")]
    class YourClass : NSObject {}
    
  • Exportieren generischer Methoden:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo")]
        void Foo<T> () {}
    }
    

Einschränkungen des neuen registrar

Einige Dinge, die Sie beim neuen registrarbeachten sollten:

  • Einige Bibliotheken von Drittanbietern müssen aktualisiert werden, damit sie mit dem neuen Registrierungssystem arbeiten können. Weitere Informationen finden Sie weiter unten unter erforderliche Änderungen .

  • Ein kurzfristiger Nachteil ist auch, dass Clang verwendet werden muss, wenn das Kontoframework verwendet wird (dies liegt daran, dass der Konto.h-Header von Apple nur von Clang kompiliert werden kann). Fügen Sie --compiler:clang den zusätzlichen mtouch-Argumenten hinzu, um Clang zu verwenden, wenn Sie Xcode 4.6 oder früher verwenden (Xamarin.iOS wählt automatisch Clang in Xcode 5.0 oder höher aus.)

  • Wenn Xcode 4.6 (oder früher) verwendet wird, muss GCC/G++ ausgewählt werden, wenn exportierte Typnamen Nicht-ASCII-Zeichen enthalten (dies liegt daran, dass die mit Xcode 4.6 ausgelieferte Version von Clang keine Nicht-ASCII-Zeichen in Bezeichnern im Objective-C Code unterstützt). Fügen Sie --compiler:gcc den zusätzlichen mtouch-Argumenten hinzu, um GCC zu verwenden.

Auswählen eines registrar

Sie können eine andere registrar Option auswählen, indem Sie den zusätzlichen mtouch-Argumenten in den iOS-Buildeinstellungen des Projekts eine der folgenden Optionen hinzufügen:

  • --registrar:static – Standard für Gerätebuilds
  • --registrar:dynamic – Standard für Simulatorbuilds

Hinweis

Die klassische API von Xamarin unterstützte andere Optionen wie --registrar:legacystatic und --registrar:legacydynamic. Diese Optionen werden jedoch von der einheitlichen API nicht unterstützt.

Mängel im alten Registrierungssystem

Das alte Registrierungssystem hat folgende Nachteile:

  • Es gab keinen (nativen) statischen Verweis auf Objective-C Klassen und Methoden in nativen Drittanbieterbibliotheken, was bedeutet, dass wir den nativen Linker nicht bitten konnten, nativen Drittanbietercode zu entfernen, der nicht tatsächlich verwendet wurde (da alles entfernt würde). Dies ist der Grund dafür -force_load libNative.a , dass jede Drittanbieterbindung (oder das Äquivalent ForceLoad=true im [LinkWith] Attribut) tun musste.
  • Sie können zwei verwaltete Typen mit demselben Objective-C Namen ohne Warnung exportieren. Ein seltenes Szenario bestand darin, zwei AppDelegate Klassen in unterschiedlichen Namespaces zu verwenden. Zur Laufzeit wäre es völlig zufällig, welche App ausgewählt wurde (in der Tat variierte es zwischen den Ausführungen einer App, die nicht einmal neu erstellt wurde - was zu einer sehr rätselhaften und frustrierenden Debugerfahrung führte).
  • Sie können zwei Methoden mit derselben Objective-C Signatur exportieren. Wieder einmal, von dem aus aufgerufen Objective-C wurde, war zufällig (aber dieses Problem war nicht so häufig wie das vorherige, vor allem, weil die einzige Möglichkeit, diesen Fehler tatsächlich zu erleben, darin bestand, die unglücklich verwaltete Methode zu überschreiben).
  • Der Satz von Methoden, der exportiert wurde, unterschied sich geringfügig zwischen dynamischen und statischen Builds.
  • Es funktioniert nicht ordnungsgemäß beim Exportieren generischer Klassen (die genaue generische Implementierung, die zur Laufzeit ausgeführt wird, wäre zufällig, was zu einem unbestimmten Verhalten führt).

Neu registrar: Erforderliche Änderungen an Bindungen

In diesem Abschnitt werden Bindungsänderungen beschrieben, die vorgenommen werden müssen, um mit dem neuen registrarzu arbeiten.

Protokolle müssen über das Attribut [Protocol] verfügen.

Protokolle müssen jetzt über das [Protocol] -Attribut verfügen. Wenn Sie dies nicht tun, erhalten Sie einen nativen Linkerfehler, z. B.:

Undefined symbols for architecture i386: "_OBJC_CLASS_$_ProtocolName", referenced from: ...

Selektoren müssen über eine gültige Anzahl von Parametern verfügen.

Alle Selektoren müssen die Anzahl der Parameter korrekt angeben. Zuvor wurden diese Fehler ignoriert und könnten Laufzeitprobleme verursachen.

Kurz gesagt, die Anzahl der Doppelpunkte muss mit der Anzahl von Parametern übereinstimmen:

  • Keine Parameter: foo
  • Ein Parameter: foo:
  • Zwei Parameter: foo:parameterName2:

Im Folgenden werden falsche Verwendungen verwendet:

// Invalid: export takes no arguments, but function expects one
[Export ("apply")]
void Apply (NSObject target);

// Invalid: exported as taking an argument, but the managed version does not have one:
[Export ("display:")]
void Display ();

Verwenden des IsVariadic-Parameters im Export

Variatische Funktionen müssen das IsVariadic Argument für das [Export] -Attribut verwenden:

[Export ("variadicMethod:", IsVariadic = true)]
void VariadicMethod (NSObject first, IntPtr subsequent);

Es ist unmöglich, Klassen zu binden, die in der nativen Bibliothek nicht vorhanden sind. Wenn eine Klasse aus der nativen Bibliothek entfernt oder umbenannt wurde, müssen Sie die Bindungen so aktualisieren, dass sie übereinstimmen.