Tipo registrar per Xamarin.iOS

Questo documento descrive il sistema di registrazione dei tipi usato da Xamarin.iOS.

Registrazione di classi e metodi gestiti

Durante l'avvio, Xamarin.iOS registrerà:

Si consideri ad esempio il metodo gestito Main comune nelle applicazioni Xamarin.iOS:

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

Questo codice indica al Objective-C runtime di usare il tipo denominato AppDelegate come classe delegato dell'applicazione. Affinché il Objective-C runtime sia in grado di creare un'istanza della classe C# AppDelegate , tale classe deve essere registrata.

Xamarin.iOS esegue automaticamente la registrazione, in fase di esecuzione (registrazione dinamica) o in fase di compilazione (registrazione statica).

La registrazione dinamica usa la reflection all'avvio per trovare tutte le classi e i metodi da registrare, passandoli al Objective-C runtime. La registrazione dinamica viene usata per impostazione predefinita per le compilazioni del simulatore.

La registrazione statica controlla, in fase di compilazione, gli assembly usati dall'applicazione. Determina le classi e i metodi con cui eseguire la registrazione Objective-C e genera una mappa incorporata nel file binario. Quindi, all'avvio, registra la mappa con il Objective-C runtime. La registrazione statica viene usata per le compilazioni dei dispositivi.

Categorie

A partire da Xamarin.iOS 8.10, è possibile creare Objective-C categorie usando la sintassi C#.

Per creare una categoria, usare l'attributo [Category] e specificare il tipo da estendere. Ad esempio, il codice seguente estende NSString:

[Category (typeof (NSString))]

Ognuno dei metodi di una categoria ha un [Export] attributo, rendendolo disponibile per il Objective-C runtime:

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

Tutti i metodi di estensione gestita devono essere statici, ma è possibile creare Objective-C metodi di istanza usando la sintassi C# standard per i metodi di estensione:

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

Il primo argomento del metodo di estensione è l'istanza in cui è stato richiamato il metodo:

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

In questo esempio verrà aggiunto un metodo di istanza nativa toUpper alla NSString classe . Questo metodo può essere chiamato da Objective-C:

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

Protocolli

A partire da Xamarin.iOS 8.10, le interfacce con l'attributo [Protocol] verranno esportate in Objective-C come protocolli:

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

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

Questo codice viene esportato IMyProtocolObjective-C in come protocollo denominato MyProtocol e una classe denominata MyClass che implementa il protocollo.

Nuovo sistema di registrazione

A partire dalla versione stabile 6.2.6 e dalla versione beta 6.3.4, è stata aggiunta una nuova versione statica registrar. Nella versione 7.2.1 è stata apportata la nuova registrar impostazione predefinita.

Questo nuovo sistema di registrazione offre le nuove funzionalità seguenti:

  • Rilevamento in fase di compilazione degli errori del programmatore:

    • Due classi registrate con lo stesso nome.
    • Più metodi esportati per rispondere allo stesso selettore
  • Rimozione del codice nativo inutilizzato:

    • Il nuovo sistema di registrazione aggiungerà riferimenti sicuri al codice usato nelle librerie statiche, consentendo al linker nativo di rimuovere il codice nativo inutilizzato dal file binario risultante. Nelle associazioni di esempio di Xamarin la maggior parte delle applicazioni diventa almeno 300.000.
  • Supporto per sottoclassi generiche di NSObject. Per altre informazioni, vedere NSObject Generics ( Generics NSObject). Inoltre, il nuovo sistema di registrazione intercetta costrutti generici non supportati che in precedenza avrebbero causato un comportamento casuale in fase di esecuzione.

Errori rilevati dal nuovo registrar

Di seguito sono riportati alcuni esempi degli errori rilevati dal nuovo registraroggetto .

  • Esportazione dello stesso selettore più volte nella stessa classe:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo:")]
        void Foo (NSString str);
        [Export ("foo:")]
        void Foo (string str)
    }
    
  • Esportazione di più classi gestite con lo stesso Objective-C nome:

    [Register ("Class")]
    class MyClass : NSObject {}
    
    [Register ("Class")]
    class YourClass : NSObject {}
    
  • Esportazione di metodi generici:

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

Limitazioni del nuovo registrar

Alcuni aspetti da tenere presenti sulla nuova registrar:

  • Alcune librerie di terze parti devono essere aggiornate per funzionare con il nuovo sistema di registrazione. Per altri dettagli, vedere le modifiche necessarie di seguito.

  • Uno svantaggio a breve termine è anche che Clang deve essere usato se viene usato il framework Accounts (perché l'intestazione accounts.h di Apple può essere compilata solo da Clang). Aggiungere --compiler:clang agli argomenti mtouch aggiuntivi per usare Clang se si usa Xcode 4.6 o versioni precedenti (Xamarin.iOS selezionerà automaticamente Clang in Xcode 5.0 o versione successiva).

  • Se si usa Xcode 4.6 (o versioni precedenti), È necessario selezionare GCC/G++ se i nomi dei tipi esportati contengono caratteri non ASCII (perché la versione di Clang fornita con Xcode 4.6 non supporta caratteri non ASCII all'interno degli identificatori nel Objective-C codice). Aggiungere --compiler:gcc agli argomenti mtouch aggiuntivi per usare GCC.

Selezione di un oggetto registrar

È possibile selezionare un'altra opzione registrar aggiungendo una delle opzioni seguenti agli argomenti mtouch aggiuntivi nelle impostazioni di compilazione iOS del progetto:

  • --registrar:static : impostazione predefinita per le compilazioni dei dispositivi
  • --registrar:dynamic : impostazione predefinita per le compilazioni del simulatore

Nota

L'API classica di Xamarin supporta altre opzioni, --registrar:legacystatic ad esempio e --registrar:legacydynamic. Tuttavia, queste opzioni non sono supportate dall'API unificata.

Carenze nel vecchio sistema di registrazione

Il sistema di registrazione precedente presenta i seguenti svantaggi:

  • Non esiste un riferimento statico (nativo) a Objective-C classi e metodi nelle librerie native di terze parti, il che significa che non è stato possibile chiedere al linker nativo di rimuovere il codice nativo di terze parti non effettivamente usato (perché tutto verrebbe rimosso). Questo è il motivo per cui -force_load libNative.a ogni associazione di terze parti doveva eseguire (o l'equivalente ForceLoad=true nell'attributo [LinkWith] ).
  • È possibile esportare due tipi gestiti con lo stesso Objective-C nome senza alcun avviso. Uno scenario raro era quello di finire con due AppDelegate classi in spazi dei nomi diversi. In fase di esecuzione sarebbe completamente casuale quale sia stato selezionato (in realtà varia tra le esecuzioni di un'app che non è stata nemmeno ricompilata, che ha fatto per un'esperienza di debug molto sconcertante e frustrante).
  • È possibile esportare due metodi con la stessa Objective-C firma. Ancora una volta da cui sarebbe stato chiamato Objective-C uno era casuale (ma questo problema non era così comune come quello precedente, principalmente perché l'unico modo per sperimentare effettivamente questo bug era quello di eseguire l'override del metodo gestito sfortunato).
  • Il set di metodi esportati è leggermente diverso tra compilazioni dinamiche e statiche.
  • Non funziona correttamente quando si esportano classi generiche (che l'implementazione generica esatta eseguita in fase di esecuzione sarebbe casuale, con conseguente comportamento non deterministico).

Nuovo registrar: modifiche necessarie alle associazioni

In questa sezione vengono descritte le modifiche alle associazioni che devono essere apportate per lavorare con il nuovo registraroggetto .

I protocolli devono avere l'attributo [Protocollo]

I protocolli devono ora avere l'attributo [Protocol] . In caso contrario, verrà generato un errore del linker nativo, ad esempio:

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

I selettori devono avere un numero valido di parametri

Tutti i selettori devono indicare correttamente il numero di parametri. In precedenza, questi errori venivano ignorati e potevano causare problemi di runtime.

In breve, il numero di due punti deve corrispondere al numero di parametri:

  • Nessun parametro: foo
  • Un parametro: foo:
  • Due parametri: foo:parameterName2:

Di seguito sono riportati gli utilizzi non corretti:

// 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 ();

Usare il parametro IsVariadic in Export

Le funzioni variadic devono usare l'argomento IsVariadic per l'attributo [Export] :

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

Non è possibile associare classi che non esistono nella libreria nativa. Se una classe è stata rimossa o rinominata nella libreria nativa, assicurarsi di aggiornare le associazioni in modo che corrispondano.