Tipo registrar para Xamarin.iOS

En este documento se describe el sistema de registro de tipos usado por Xamarin.iOS.

Registro de clases y métodos administrados

Durante el inicio, Xamarin.iOS registrará:

  • Clases con un atributo[Register] como clases Objective-C.
  • Clases con un atributo [Category] como categorías Objective-C.
  • Interfaces con un atributo [Protocol] como protocolos Objective-C.
  • Miembros con un [Export], lo que permite a Objective-C acceder a ellos.

Por ejemplo, considere el método común Main administrado en aplicaciones de Xamarin.iOS:

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

Este código indica al tiempo de ejecución de Objective-C que use el tipo llamado AppDelegate como clase de delegado de la aplicación. Para que el entorno de ejecución de Objective-C pueda crear una instancia de la clase AppDelegate de C#, esa clase debe registrarse.

Xamarin.iOS realiza el registro automáticamente, ya sea en tiempo de ejecución (registro dinámico) o en tiempo de compilación (registro estático).

El registro dinámico usa la reflexión en el inicio para buscar todas las clases y métodos que se van a registrar y pasarlas al tiempo de ejecución de Objective-C. El registro dinámico se usa de forma predeterminada para las compilaciones del simulador.

El registro estático inspecciona, en tiempo de compilación, los ensamblados usados por la aplicación. Determina las clases y los métodos para registrarse con Objective-C y genera un mapa, que se inserta en el archivo binario. A continuación, en el inicio, registra el mapa con el tiempo de ejecución de Objective-C. El registro estático se usa para compilaciones de dispositivos.

Categorías

A partir de Xamarin.iOS 8.10, es posible crear categorías de Objective-C desde la sintaxis de C#.

Para crear una categoría, use el atributo [Category] y especifique el tipo que se va a extender. Por ejemplo, el código siguiente amplía NSString:

[Category (typeof (NSString))]

Cada uno de los métodos de una categoría tiene un atributo [Export], lo que hace que esté disponible para el tiempo de ejecución de Objective-C:

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

Todos los métodos de extensión administrados deben ser estáticos, pero es posible crear métodos de instancia de Objective-C con la sintaxis estándar de C# para los métodos de extensión:

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

El primer argumento del método de extensión es la instancia en la que se invocó el método:

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

En este ejemplo se agregará un método de instancia nativo toUpper a la clase NSString. Se puede llamar a este método desde Objective-C:

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

Protocolos

A partir de Xamarin.iOS 8.10, las interfaces con el atributo [Protocol] se exportarán a Objective-C como protocolos:

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

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

Este código exporta IMyProtocol a Objective-C como un protocolo denominado MyProtocol y una clase denominada MyClass que implementa el protocolo.

Nuevo sistema de registro

A partir de la versión estable 6.2.6 y la versión beta 6.3.4, hemos agregado un nuevo estático registrar. En la versión 7.2.1, hemos hecho que registrar sea el nuevo valor predeterminado.

Este nuevo sistema de registro ofrece las siguientes características nuevas:

  • Detección en tiempo de compilación de errores del programador:

    • Dos clases que se registran con el mismo nombre.
    • Más de un método exportado para responder al mismo selector
  • Eliminación del código nativo sin usar:

    • El nuevo sistema de registro agregará referencias seguras al código usado en bibliotecas estáticas, lo que permite que el enlazador nativo quite el código nativo sin usar del binario resultante. En los enlaces de ejemplo de Xamarin, la mayoría de las aplicaciones se convierten al menos en 300 000 menos.
  • Compatibilidad con subclases genéricas de NSObject; vea NSObject Generics para obtener más información. Además, el nuevo sistema de registro detectará construcciones genéricas no admitidas que habrían causado previamente un comportamiento aleatorio en tiempo de ejecución.

Errores detectados por el nuevo registrar

A continuación se muestran algunos ejemplos de los errores detectados por el nuevo registrar.

  • Exportar el mismo selector más de una vez en la misma clase:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo:")]
        void Foo (NSString str);
        [Export ("foo:")]
        void Foo (string str)
    }
    
  • Exportar más de una clase administrada con el mismo nombre de Objective-C:

    [Register ("Class")]
    class MyClass : NSObject {}
    
    [Register ("Class")]
    class YourClass : NSObject {}
    
  • Exportación de métodos genéricos:

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

Limitaciones del nuevo registrar

Algunas cosas que hay que tener en cuenta sobre el nuevo registrar:

  • Algunas bibliotecas de terceros deben actualizarse para trabajar con el nuevo sistema de registro. Consulte las modificaciones necesarias a continuación para obtener más detalles.

  • Una desventaja a corto plazo también es que Clang debe usarse si se usa el marco Accounts (esto se debe a que el encabezado accounts.h de Apple solo se puede compilar mediante Clang). Agregue --compiler:clang a los argumentos mtouch adicionales para usar Clang si usa Xcode 4.6 o versiones anteriores (Xamarin.iOS seleccionará automáticamente Clang en Xcode 5.0 o posterior).

  • Si se usa Xcode 4.6 (o versiones anteriores), se debe seleccionar GCC/G++ si los nombres de tipo exportados contienen caracteres no ASCII (esto se debe a que la versión de Clang enviada con Xcode 4.6 no admite caracteres no ASCII dentro de identificadores en el código Objective-C). Agregue --compiler:gcc a los argumentos mtouch adicionales para usar GCC.

Selección de un registrar

Puede seleccionar otro registrar si agrega una de las siguientes opciones a los argumentos mtouch adicionales en la configuración de compilación de iOSdel proyecto:

  • --registrar:static: valor predeterminado para compilaciones de dispositivos
  • --registrar:dynamic: valor predeterminado para las compilaciones del simulador

Nota:

La API clásica de Xamarin admite otras opciones, como --registrar:legacystatic y --registrar:legacydynamic. Sin embargo, la API unificada no admite estas opciones.

Deficiencias en el antiguo sistema de registro

El sistema de registro antiguo tiene las siguientes desventajas:

  • No había ninguna referencia estática (nativa) a clases y métodos de Objective-C en bibliotecas nativas de terceros, lo que significaba que no se podía pedir al enlazador nativo que quitara código nativo de terceros que no se usara realmente (porque todo se quitaría). Esta es la razón de -force_load libNative.a que todos los enlaces de terceros tenían que hacer (o el equivalente ForceLoad=true en el atributo [LinkWith]).
  • Puede exportar dos tipos administrados con el mismo nombre Objective-C sin advertencia. Un escenario poco frecuente era terminar con dos clases AppDelegate en diferentes espacios de nombres. En tiempo de ejecución sería completamente aleatorio el que se cogió (de hecho, varía entre ejecuciones de una aplicación que ni siquiera se recompiló, lo que hizo para una experiencia de depuración muy aproximada y frustrante).
  • Puede exportar dos métodos con la misma Objective-C firma. Una vez más, a la que se llamaría desde Objective-C era aleatoria (pero este problema no era tan común como el anterior, principalmente porque la única manera de experimentar realmente este error era invalidar el método administrado desafortunado).
  • El conjunto de métodos exportados era ligeramente diferente entre compilaciones dinámicas y estáticas.
  • No funciona correctamente al exportar clases genéricas (la implementación genérica exacta ejecutada en tiempo de ejecución sería aleatoria, lo que da lugar de forma eficaz a un comportamiento indeterminado).

Nuevo registrar: cambios necesarios en los enlaces

En esta sección se describen los cambios de enlace que se deben realizar para poder trabajar con el nuevo registrar.

Los protocolos deben tener el atributo [Protocol]

Los protocolos ahora deben tener el atributo [Protocol]. Si no lo hace, se producirá un error nativo del enlazador, como:

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

Los selectores deben tener un número válido de parámetros

Todos los selectores deben indicar el número de parámetros correctamente. Anteriormente, estos errores se ignoraban y podían causar problemas en tiempo de ejecución.

En resumen, el número de dos puntos debe coincidir con el número de parámetros:

  • No hay parámetros: foo
  • Un parámetro: foo:
  • Dos parámetros: foo:parameterName2:

A continuación se muestran usos incorrectos:

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

Uso del parámetro IsVariadic en Export

Las funciones Variadic deben usar el argumento IsVariadic para el atributo [Export]:

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

Es imposible enlazar clases que no existen en la biblioteca nativa. Si se ha quitado o cambiado el nombre de una clase en la biblioteca nativa, asegúrese de actualizar los enlaces para que coincidan.