Архитектура приложений iOS

Приложения Xamarin.iOS выполняются в среде выполнения Mono и используют полную компиляцию перед временем (AOT) для компиляции кода C# на язык сборок ARM. Это выполняется параллельно со средой Objective-C выполнения. Обе среды выполнения выполняются поверх ядра UNIX, например XNU, и предоставляют различные API-интерфейсы пользовательскому коду, позволяя разработчикам получать доступ к базовой собственной или управляемой системе.

На следующей схеме показан базовый обзор этой архитектуры:

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

Собственный и управляемый код: объяснение

При разработке для Xamarin часто используются собственный и управляемый код. Управляемый код — это код, который управляется платформа .NET Framework средой CLR или в случае Xamarin: среда выполнения Mono. Это то, что мы называем промежуточным языком.

Машинный код — это код, который будет выполняться в собственном коде на конкретной платформе (например, Objective-C или даже скомпилированный код AOT на микросхеме ARM). В этом руководстве описывается, как AOT компилирует управляемый код в машинный код и объясняет, как работает приложение Xamarin.iOS, что позволяет полностью использовать API iOS Apple через использование привязок, а также доступ к. BCL NET и сложный язык, например C#.

AOT

При компиляции любого приложения платформы Xamarin компилятор Mono C# (или F#) будет выполняться и компилирует код C# и F# в msIL. Если вы используете приложение Xamarin.Android, приложение Xamarin.Mac или даже приложение Xamarin.iOS на симуляторе, среда CLR .NET компилирует MSIL с помощью JIT-компилятора. Во время выполнения этот код компилируется в машинный код, который может выполняться в правильной архитектуре приложения.

Однако существует ограничение безопасности на iOS, установленное Apple, которое запрещает выполнение динамически созданного кода на устройстве. Чтобы обеспечить соблюдение этих протоколов безопасности, Xamarin.iOS вместо этого использует компилятор AOT для компиляции управляемого кода. Это создает собственный двоичный файл iOS, при необходимости оптимизированный для LLVM для устройств, который можно развернуть на процессоре на основе ARM Apple. Шероховая схема того, как это подходит вместе, показана ниже:

A rough diagram of how this fits together

Использование AOT имеет ряд ограничений, которые подробно описаны в руководстве по ограничениям . Он также предоставляет ряд улучшений по JIT за счет сокращения времени запуска и различных оптимизаций производительности.

Теперь, когда мы изучили, как код компилируется из исходного кода в машинный код, давайте рассмотрим, как Xamarin.iOS позволяет создавать полностью собственные приложения iOS.

Селекторы

С Xamarin у нас есть две отдельные экосистемы, .NET и Apple, что нам нужно объединить, чтобы показаться как можно более упрощенным, чтобы гарантировать, что конечная цель является гладким взаимодействием с пользователем. Мы видели в разделе выше, как два среды выполнения взаимодействуют, и вы, возможно, очень хорошо слышали о термине "привязки", который позволяет использовать собственные API iOS в Xamarin. Привязки подробно описаны в нашей Objective-C документации по привязке , поэтому теперь рассмотрим, как работает iOS под капюшоном.

Во-первых, существует способ предоставления Objective-C доступа к C#, который выполняется с помощью селекторов. Селектор — это сообщение, которое отправляется в объект или класс. При Objective-C этом выполняется с помощью функций objc_msgSend . Дополнительные сведения об использовании селекторов см. в руководстве по селекторамObjective-C. Существует также способ предоставления управляемого кода Objective-C, который является более сложным из-за того, что Objective-C ничего не известно об управляемом коде. Чтобы обойти это, мы используем Registrars. Более подробно описаны в следующем разделе.

Registrars

Как упоминание выше, это registrar код, предоставляющий управляемый кодObjective-C. Это делается путем создания списка каждого управляемого класса, наследуемого от NSObject:

  • Для всех классов, которые не упаковывают существующий Objective-C класс, он создает новый Objective-C класс с Objective-C элементами, зеркало всех управляемых элементов, имеющих атрибут [Export] .

  • В реализациях для каждого члена Objective–C код добавляется автоматически для вызова зеркало управляемого члена.

В приведенном ниже псевдокоде показано, как это сделать:

C# (управляемый код)

 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

Управляемый код может содержать атрибуты и [Register][Export], что registrar используется для того, чтобы знать, что объекту необходимо предоставить Objective-Cдоступ. Атрибут [Register] используется для указания имени созданного Objective-C класса в случае, если созданное по умолчанию имя не подходит. Все классы, производные от NSObject, автоматически регистрируются в Objective-C. Обязательный [Export] атрибут содержит строку, которая является селектором, используемым в созданном Objective-C классе.

В Xamarin.iOS используется два типа: динамический и статический registrars :

  • Dynamic — dynamic registrarsregistrar выполняет регистрацию всех типов в сборке во время выполнения. Это делается с помощью функций, предоставляемых Objective-CAPI среды выполнения. Следовательно, динамический registrar имеет более медленный запуск, но более быстрое время сборки. Это значение по умолчанию для симулятора iOS. Собственные функции (обычно в C), называемые батутами, используются в качестве реализаций методов при использовании динамической registrars. Они различаются между различными архитектурами.

  • Статический — статический registrarsregistrar создает Objective-C код во время сборки, который затем компилируется в статическую библиотеку и связан с исполняемым файлом. Это позволяет ускорить запуск, но занимает больше времени во время сборки. Это используется по умолчанию для сборок устройств. Статические registrar также можно использовать с симулятором iOS, передав --registrar:static в качестве mtouch атрибута в параметрах сборки проекта, как показано ниже:

    Setting Additional mtouch arguments

Дополнительные сведения о специфике системы регистрации типов iOS, используемой Xamarin.iOS, см. в руководстве по типуRegistrar.

Запуск приложения

Точка входа всех исполняемых файлов Xamarin.iOS предоставляется функцией, которая xamarin_mainинициализирует mono.

В зависимости от типа проекта выполняется следующее:

  • Для обычных приложений iOS и tvOS вызывается управляемый метод Main, предоставляемый приложением Xamarin. Затем этот управляемый метод Main вызывает UIApplication.Main, который является точкой входа для Objective-C. UIApplication.Main — это привязка методаObjective-CUIApplicationMain.
  • Для расширений вызывается собственная функция или ( NSExtensionMainNSExtensionmain для расширений WatchOS), предоставляемая библиотеками Apple. Так как эти проекты являются библиотеками классов и не исполняемыми проектами, для выполнения управляемых методов Main нет.

Вся эта последовательность запуска компилируется в статическую библиотеку, которая затем связана с окончательным исполняемым файлом, чтобы ваше приложение знали, как выйти из земли.

На этом этапе приложение запущено, Mono работает, мы в управляемом коде, и мы знаем, как вызывать машинный код и вызывать обратно. Далее мы должны сделать, чтобы начать добавлять элементы управления и сделать приложение интерактивным.

Генератор

Xamarin.iOS содержит определения для каждого отдельного API iOS. Вы можете просмотреть любой из них в репозитории GitHub MaciOS. Эти определения содержат интерфейсы с атрибутами, а также любые необходимые методы и свойства. Например, следующий код используется для определения панели пользовательского интерфейса в пространстве имен UIKit. Обратите внимание, что это интерфейс с рядом методов и свойств:

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

    ...
}

Генератор, называемый btouch в Xamarin.iOS, принимает эти файлы определения и использует средства .NET для компиляции их во временную сборку. Однако эта временная сборка не используется для вызова Objective-C кода. Затем генератор считывает временную сборку и создает код C#, который можно использовать во время выполнения. Поэтому, например, если добавить случайный атрибут в файл определения .cs, он не будет отображаться в выходном коде. Генератор не знает об этом и поэтому btouch не знает, чтобы искать его во временной сборке, чтобы вывести его.

После создания Xamarin.iOS.dll mtouch упаковает все компоненты вместе.

На высоком уровне это достигается путем выполнения следующих задач:

  • Создайте структуру пакета приложений.
  • Скопируйте в управляемые сборки.
  • Если связывание включено, запустите управляемый компоновщик для оптимизации сборок путем копирования неиспользуемых частей.
  • Компиляция AOT.
  • Создайте собственный исполняемый файл, который выводит ряд статических библиотек (по одному для каждой сборки), связанный с собственным исполняемым файлом, чтобы собственный исполняемый файл состоял из кода средства запуска, registrar кода (если статический) и всех выходных данных компилятора AOT

Дополнительные сведения об компоновщике и его использовании см. в руководстве компоновщика .

Итоги

В этом руководстве подробно рассматривается компиляция приложений Xamarin.iOS и подробно рассматриваются Objective-C Xamarin.iOS.