Arquitectura de aplicaciones de iOS

Las aplicaciones de Xamarin.iOS se ejecutan en el entorno de ejecución mono y usan la compilación completa De antemano (AOT) para compilar código de C# en el lenguaje de ensamblado arm. Esto se ejecuta en paralelo con el entorno de Objective-C ejecución. Ambos entornos de tiempo de ejecución se ejecutan sobre un kernel similar a UNIX, específicamente XNU, y exponen varias API al código de usuario, lo que permite a los desarrolladores acceder al sistema nativo o administrado subyacente.

En el diagrama siguiente se muestra información general básica de esta arquitectura:

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

Código nativo y administrado: explicación

Al desarrollar para Xamarin, a menudo se usan los términos código nativo y administrado . El código administrado es código que tiene su ejecución administrada por Common Language Runtime de .NET Framework o, en el caso de Xamarin: Mono Runtime. Esto es lo que llamamos un lenguaje intermedio.

El código nativo es código que se ejecutará de forma nativa en la plataforma específica (por ejemplo, Objective-C o incluso código compilado AOT, en un chip ARM). En esta guía se explora cómo AOT compila el código administrado en código nativo y se explica cómo funciona una aplicación de Xamarin.iOS, haciendo uso completo de las API de iOS de Apple mediante el uso de enlaces, al mismo tiempo que tiene acceso a . BCL de NET y un lenguaje sofisticado como C#.

AOT

Al compilar cualquier aplicación de plataforma de Xamarin, el compilador de Mono C# (o F#) se ejecutará y compilará el código de C# y F# en lenguaje intermedio de Microsoft (MSIL). Si ejecuta una aplicación de Xamarin.Android, una aplicación de Xamarin.Mac o incluso una aplicación de Xamarin.iOS en el simulador, Common Language Runtime (CLR) de .NET compila MSIL mediante un compilador Just-In-Time (JIT). En tiempo de ejecución, se compila en un código nativo, que se puede ejecutar en la arquitectura correcta para la aplicación.

Sin embargo, hay una restricción de seguridad en iOS, establecida por Apple, que no permite la ejecución de código generado dinámicamente en un dispositivo. Para asegurarnos de que se adhieren a estos protocolos de seguridad, Xamarin.iOS usa en su lugar un compilador De antemano (AOT) para compilar el código administrado. Esto genera un binario nativo de iOS, opcionalmente optimizado con LLVM para dispositivos, que se puede implementar en el procesador basado en ARM de Apple. A continuación se muestra un diagrama aproximado de cómo encaja esto:

A rough diagram of how this fits together

El uso de AOT tiene varias limitaciones, que se detallan en la guía limitaciones . También proporciona varias mejoras sobre JIT a través de una reducción del tiempo de inicio y varias optimizaciones de rendimiento.

Ahora que hemos explorado cómo se compila el código de origen a código nativo, echemos un vistazo en segundo plano para ver cómo Xamarin.iOS nos permite escribir aplicaciones iOS totalmente nativas.

Selectores

Con Xamarin, tenemos dos ecosistemas independientes, .NET y Apple, que necesitamos reunir para parecer lo más simplificado posible, para garantizar que el objetivo final sea una experiencia de usuario fluida. Hemos visto en la sección anterior cómo se comunican los dos entornos de ejecución, y es posible que haya oído hablar muy bien del término "enlaces", lo que permite que las API nativas de iOS se usen en Xamarin. Los enlaces se explican en profundidad en nuestra Objective-C documentación de enlace , por lo que por ahora vamos a explorar cómo funciona iOS en segundo plano.

En primer lugar, debe haber una manera de exponer Objective-C a C#, que se realiza a través de selectores. Un selector es un mensaje que se envía a un objeto o clase. Con Objective-C esto se realiza a través de las funciones de objc_msgSend . Para obtener más información sobre el uso de selectores, consulte la Objective-C guía Selectores . También debe haber una manera de exponer código administrado a Objective-C, lo que es más complicado debido al hecho de que Objective-C no sabe nada sobre el código administrado. Para desplazarse por esto, usamos Registrars. Estos se explican con más detalle en la sección siguiente.

Registrars

Como se mencionó anteriormente, registrar es el código que expone código administrado a Objective-C. Para ello, crea una lista de todas las clases administradas que derivan de NSObject:

  • Para todas las clases que no encapsulan una clase existente Objective-C , crea una nueva Objective-C clase con Objective-C miembros que reflejan todos los miembros administrados que tienen un atributo [Export].

  • En las implementaciones de cada miembro Objective-C, el código se agrega automáticamente para llamar al miembro administrado reflejado.

El pseudocódigo siguiente muestra un ejemplo de cómo se hace esto:

C# (código administrado)

 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

El código administrado puede contener los atributos [Register] y [Export], que usa registrar para saber que el objeto debe exponerse a Objective-C. El [Register] atributo se usa para especificar el nombre de la clase generada Objective-C en caso de que el nombre generado predeterminado no sea adecuado. Todas las clases derivadas de NSObject se registran automáticamente con Objective-C. El atributo obligatorio [Export] contiene una cadena, que es el selector usado en la clase generada Objective-C .

Hay dos tipos de registrars uso en Xamarin.iOS: dinámicos y estáticos:

  • Dinámico registrars : el dinámico registrar realiza el registro de todos los tipos del ensamblado en tiempo de ejecución. Para ello, usa las funciones proporcionadas por Objective-Cla API en tiempo de ejecución. Por lo tanto, la dinámica registrar tiene un inicio más lento, pero un tiempo de compilación más rápido. Este es el valor predeterminado para el simulador de iOS. Las funciones nativas (normalmente en C), llamadas trampolines, se usan como implementaciones de método cuando se usa el dinámico registrars. Varían entre diferentes arquitecturas.

  • Static registrars : el código estático registrar genera Objective-C código durante la compilación, que luego se compila en una biblioteca estática y se vincula al archivo ejecutable. Esto permite un inicio más rápido, pero tarda más tiempo durante el tiempo de compilación. Esto se usa de forma predeterminada para las compilaciones de dispositivos. El estático registrar también se puede usar con el simulador de iOS pasando --registrar:static como atributo mtouch en las opciones de compilación del proyecto, como se muestra a continuación:

    Setting Additional mtouch arguments

Para obtener más información sobre los detalles del sistema de registro de tipos de iOS usado por Xamarin.iOS, consulte la guía de tipos Registrar .

Inicio de la aplicación

El punto de entrada de todos los ejecutables de Xamarin.iOS lo proporciona una función denominada xamarin_main, que inicializa mono.

Según el tipo de proyecto, se realiza lo siguiente:

  • En el caso de las aplicaciones normales de iOS y tvOS, se llama al método Main administrado proporcionado por la aplicación Xamarin. A continuación, este método Main administrado llama a UIApplication.Main, que es el punto de entrada de Objective-C. UIApplication.Main es el enlace para Objective-Cel método de UIApplicationMain .
  • En el caso de las extensiones, se llama a la función nativa ( NSExtensionMain o (NSExtensionmain para extensiones watchOS) proporcionada por las bibliotecas de Apple. Dado que estos proyectos son bibliotecas de clases y no proyectos ejecutables, no hay métodos Main administrados para ejecutar.

Toda esta secuencia de inicio se compila en una biblioteca estática, que luego se vincula al archivo ejecutable final para que la aplicación sepa cómo ponerse en marcha.

En este momento, nuestra aplicación se ha iniciado, Mono se está ejecutando, estamos en código administrado y sabemos cómo llamar al código nativo y volver a llamar. Lo siguiente que debemos hacer es empezar a agregar controles y hacer que la aplicación sea interactiva.

Generator

Xamarin.iOS contiene definiciones para cada API de iOS única. Puede examinar cualquiera de estos elementos en el repositorio de GitHub de MaciOS. Estas definiciones contienen interfaces con atributos, así como cualquier método y propiedades necesarios. Por ejemplo, el código siguiente se usa para definir un UIToolbar en el espacio de nombres UIKit. Observe que es una interfaz con varios métodos y propiedades:

[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);

    ...
}

El generador, llamado btouch en Xamarin.iOS, toma estos archivos de definición y usa herramientas de .NET para compilarlos en un ensamblado temporal. Sin embargo, este ensamblado temporal no se puede usar para llamar Objective-C al código. A continuación, el generador lee el ensamblado temporal y genera código de C# que se puede usar en tiempo de ejecución. Por este motivo, por ejemplo, si agrega un atributo aleatorio a la definición .cs archivo, no se mostrará en el código generado. El generador no lo sabe y, por lo tanto btouch , no sabe buscarlo en el ensamblado temporal para generarlo.

Una vez creado el Xamarin.iOS.dll, mtouch agrupará todos los componentes.

En un nivel alto, logra esto ejecutando las siguientes tareas:

  • Cree una estructura de agrupación de aplicaciones.
  • Copie en los ensamblados administrados.
  • Si la vinculación está habilitada, ejecute el enlazador administrado para optimizar los ensamblados mediante la extracción de elementos sin usar.
  • Compilación AOT.
  • Cree un archivo ejecutable nativo, que genera una serie de bibliotecas estáticas (una para cada ensamblado) que están vinculadas al ejecutable nativo, de modo que el ejecutable nativo consta del código del iniciador, el registrar código (si es estático) y todas las salidas del compilador AOT.

Para obtener información más detallada sobre el enlazador y cómo se usa, consulte la guía del enlazador .

Resumen

En esta guía se examinó la compilación AOT de aplicaciones de Xamarin.iOS y se ha explorado Xamarin.iOS y su relación con Objective-C en profundidad.