iOS 应用程序体系结构iOS App Architecture

Xamarin iOS 应用程序在 Mono 执行环境中运行,并使用完全提前(AOT)编译将代码编译C#为 ARM 汇编语言。Xamarin.iOS applications run within the Mono execution environment, and use full Ahead of Time (AOT) compilation to compile C# code to ARM assembly language. 这与目标 C 运行时并行运行。This runs side-by-side with the Objective-C Runtime. 这两个运行时环境都在类似于 UNIX 的内核(特别是XNU)的基础上运行,并向用户代码公开各种 api,使开发人员能够访问基础本机或托管系统。Both runtime environments run on top of a UNIX-like kernel, specifically XNU, and expose various APIs to the user code allowing developers to access the underlying native or managed system.

下图显示了此体系结构的基本概述:The diagram below shows a basic overview of this architecture:

本机代码和托管代码:说明Native and Managed code: An Explanation

当开发 Xamarin 时,通常使用术语 "本机" 和 "托管代码"。When developing for Xamarin the terms native and managed code are often used. 托管代码是由.NET Framework 公共语言运行时或 Xamarin 的情况下,由其执行的代码: Mono 运行时。Managed code is code that has its execution managed by the .NET Framework Common Language Runtime, or in Xamarin’s case: the Mono Runtime. 这就是我们称之为中间语言的。This is what we call an intermediate language.

本机代码是在特定平台(例如,目标平台,甚至是在 ARM 芯片上的 AOT 编译代码)上运行的代码。Native code is code that will run natively on the specific platform (for example, Objective-C or even AOT compiled code, on an ARM chip). 本指南探讨了 AOT 如何将托管代码编译为本机代码,并说明了 Xamarin iOS 应用程序的工作原理,充分利用了 Apple 的 iOS Api 的使用绑定,同时还具有对的访问权限。网络的 BCL 和复杂的语言(如C#)。This guide explores how AOT compiles your managed code to native code, and explains how a Xamarin.iOS application works, making full use of Apple’s iOS APIs through the use of bindings, while also having access to .NET’s BCL and a sophisticated language such as C#.

AOTAOT

编译任何C# Xamarin 平台应用程序时,Mono (或F#)编译器将运行,并将C#和F#代码编译为 Microsoft 中间语言(MSIL)。When you compile any Xamarin platform application, the Mono C# (or F#) compiler will run and will compile your C# and F# code into Microsoft Intermediate Language (MSIL). 如果运行的是 xamarin、Xamarin 应用程序,甚至是模拟器上的 Xamarin iOS 应用程序,则.Net 公共语言运行时(CLR)使用实时(JIT)编译器编译 MSIL。If you are running a Xamarin.Android, a Xamarin.Mac application, or even a Xamarin.iOS application on the simulator, the .NET Common Language Runtime (CLR) compiles the MSIL using a Just in Time (JIT) compiler. 在运行时,这会被编译为本机代码,该代码可在应用程序的正确体系结构上运行。At runtime this is compiled into a native code, which can run on the correct architecture for your application.

但是,iOS 上存在安全限制,由 Apple 设置,这不允许在设备上执行动态生成的代码。However, there is a security restriction on iOS, set by Apple, which disallows the execution of dynamically generated code on a device. 为了确保我们遵守这些安全协议,Xamarin 改为使用提前(AOT)编译器来编译托管代码。To ensure that we adhere to these safety protocols, Xamarin.iOS instead uses an Ahead of Time (AOT) compiler to compile the managed code. 这将生成本机 iOS 二进制文件,还可以选择使用 LLVM for 设备进行优化,这些二进制文件可以部署在 Apple 基于 ARM 的处理器上。This produces a native iOS binary, optionally optimized with LLVM for devices, that can be deployed on Apple’s ARM-based processor. 下面说明了如何将这一组合在一起的大致示意图:A rough diagram of how this fits together is illustrated below:

使用 AOT 有多种限制,这在限制指南中进行了详细介绍。Using AOT has a number of limitations, which are detailed in the Limitations guide. 它还通过缩短启动时间和各种性能优化,对 JIT 进行了大量改进。It also provides a number of improvements over JIT through a reduction in the startup time, and various performance optimizations

现在,我们已经探讨了如何将代码从源代码编译为本机代码,接下来让我们看看如何使用 Xamarin 来编写完全本机 iOS 应用程序Now that we have explored how the code is compiled from source to native code, let’s take a look under the hood to see how Xamarin.iOS allows us to write fully native iOS applications

选择器Selectors

使用 Xamarin,我们有两个独立的生态系统(.NET 和 Apple),我们需要将它们整合在一起,以确保最终目标是一个流畅的用户体验。With Xamarin, we have two separate ecosystems, .NET and Apple, that we need to bring together to seem as streamlined as possible, to ensure that the end goal is a smooth user experience. 在上面的部分中,我们看到了两个运行时的通信方式,你可能听说过术语 "绑定",它允许在 Xamarin 中使用本机 iOS Api。We have seen in the section above how the two runtimes communicate, and you may very well have heard of the term ‘bindings’ which allows the native iOS APIs to be used in Xamarin. 我们的目标-C 绑定文档深入介绍了绑定,因此,现在让我们来了解 iOS 如何工作。Bindings are explained in depth in our Objective-C binding documentation, so for now let’s explore how iOS works under the hood.

首先,必须有一种方法将目标-C 公开给C#,这是通过选择器完成的。First, there has to be a way to expose Objective-C to C#, which is done via Selectors. 选择器是发送到对象或类的消息。A selector is a message which is sent to an object or class. 对于目标-C,这是通过objc_msgSend函数实现的。With Objective-C this is done via the objc_msgSend functions. 有关使用选择器的详细信息,请参阅目标-C 选择器指南。For more information on using Selectors, refer to the Objective-C Selectors guide. 还必须有一种方法将托管代码公开给目标-C,这更复杂,因为目标-C 不知道托管代码的任何内容。There also has to be a way to expose managed code to Objective-C, which is more complicated due to the fact that Objective-C doesn’t know anything about the managed code. 为此,我们使用注册机构To get around this, we use Registrars. 下一节将对这些内容进行更详细的介绍。These are explained in more detail in the next section.

域名Registrars

如上所述,注册机构是向目标-C 公开托管代码的代码。As mentioned above, the registrar is code that exposes managed code to Objective-C. 它通过创建从 NSObject 派生的每个托管类的列表来实现此操作:It does this by creating a list of every managed class that derives from NSObject:

  • 对于未包装现有目标-C 类的所有类,它将创建一个新的目标-c 类,其中目标为 c 的成员镜像具有 [Export] 特性的所有托管成员。For all classes that are not wrapping an existing Objective-C class, it creates a new Objective-C class with Objective-C members mirroring all the managed members that have an [Export] attribute.

  • 在每个目标– C 成员的实现中,会自动添加代码以调用镜像托管成员。In the implementations for each Objective–C member, code is added automatically to call the mirrored managed member.

下面的伪代码演示了如何执行此操作的示例:The pseudo-code below shows an example of how this is done:

C#(托管代码)C# (Managed Code)

 class MyViewController : UIViewController{
     [Export ("myFunc")]
     public void MyFunc ()
     {
     }
 }

目标-C:Objective-C:

@interface MyViewController : UIViewController { }

    -(void)myFunc;
@end

@implementation MyViewController {}

    -(void) myFunc
    {
        /* code to call the managed MyViewController.MyFunc method */
    }
@end

托管代码可以包含属性,[Register][Export],注册机构使用该属性来了解需要向目标-C 公开对象。The managed code can contain the attributes, [Register] and [Export], that the registrar uses to know that the object needs to be exposed to Objective-C. 如果默认生成的名称不合适,则使用 [Register] 特性来指定生成的目标 C 类的名称。The [Register] attribute is used to specify the name of the generated Objective-C class in case the default generated name is not suitable. 派生自 NSObject 的所有类都将自动注册到目标 C。All classes derived from NSObject are automatically registered with Objective-C. 必需的 [Export] 属性包含一个字符串,该字符串是在生成的目标 C 类中使用的选择器。The required [Export] attribute contains a string, which is the selector used in the generated Objective-C class.

在 Xamarin 中使用了两种类型的注册机构–动态和静态:There are two types of registrars used in Xamarin.iOS – dynamic and static:

  • 动态注册机构–动态注册机构会在运行时注册程序集中的所有类型。Dynamic registrars – The dynamic registrar does the registration of all types in your assembly at runtime. 它通过使用由目标 C 的运行时 API提供的函数来实现此目的。It does this by using functions provided by Objective-C’s runtime API. 因此,动态注册器的启动速度较慢,但生成时间更快。The dynamic registrar therefore has a slower startup, but a faster build time. 这是 iOS 模拟器的默认设置。This is default for the iOS Simulator. 使用动态注册机构时,本机函数(通常为 C)(称为 trampolines)用作方法实现。Native functions (usually in C), called trampolines, are used as method implementations when using the dynamic registrars. 它们在不同的体系结构之间有所不同。They vary between different architectures.

  • 静态注册机构–静态注册机构在生成过程中生成目标 C 代码,然后将其编译为静态库并链接到可执行文件。Static registrars – The static registrar generates Objective-C code during the build, which is then compiled into a static library and linked into the executable. 这样就可以更快地启动,但在生成过程中花费的时间更长。This allows for a quicker startup, but takes longer during build time. 默认情况下,此设置用于设备生成。This is used by default for device builds. 静态注册器还可与 iOS 模拟器结合使用,方法是将 --registrar:static 作为 mtouch 特性传递到项目的生成选项中,如下所示:The static registrar can also be used with the iOS simulator by passing --registrar:static as an mtouch attribute in your project’s build options, as shown below:

有关 Xamarin 使用的 iOS 类型注册系统的详细信息,请参阅类型注册指南。For more information on the specifics of the iOS Type Registration system used by Xamarin.iOS, refer to the Type Registrar guide.

应用程序启动Application Launch

所有 Xamarin iOS 可执行文件的入口点由称为 xamarin_main的函数提供,该函数可初始化 mono。The entry point of all Xamarin.iOS executables is provided by a function called xamarin_main, which initializes mono.

根据项目类型,将执行以下操作:Depending on the project type, the following is done:

  • 对于常规的 iOS 和 tvOS 应用程序,将调用 Xamarin 应用提供的托管 Main 方法。For regular iOS and tvOS applications, the managed Main method, provided by the Xamarin app is called. 然后,此托管 Main 方法会调用 UIApplication.Main,这是目标 C 的入口点。This managed Main method then calls UIApplication.Main, which is the entry point for Objective-C. UIApplication 是目标 UIApplicationMain 方法的绑定。UIApplication.Main is the binding for Objective-C's UIApplicationMain method.
  • 对于扩展,将调用 Apple 库提供的本机函数- NSExtensionMain 或(WatchOS 扩展的NSExtensionmain)。For extensions, the native function – NSExtensionMain or (NSExtensionmain for WatchOS extensions) – provided by Apple libraries is called. 由于这些项目是类库而不是可执行项目,因此不需要执行托管的主要方法。Since these projects are class libraries and not executable projects, there are no managed Main methods to execute.

所有此启动序列均编译为静态库,然后将其链接到最终的可执行文件,以便您的应用程序了解如何实现基础。All of this launch sequence is compiled into a static library, which is then linked into your final executable so your app knows how to get off the ground.

此时,我们的应用程序已启动,Mono 正在运行,我们在托管代码中,我们知道如何调用本机代码并回调。At this point our app has started up, Mono is running, we are in managed code and we know how to call native code and be called back. 接下来我们要做的是实际开始添加控件,并使应用程序成为交互的。The next thing we need to do is to actually start adding controls and make the app interactive.

GeneratorGenerator

Xamarin 包含每个 iOS API 的定义。Xamarin.iOS contains definitions for every single iOS API. 可在MaciOS github存储库上浏览任何这些。You can browse through any of these on the MaciOS github repo. 这些定义包含具有属性的接口,以及任何所需的方法和属性。These definitions contain interfaces with attributes, as well as any necessary methods and properties. 例如,下面的代码用于定义 UIKit命名空间中的 UIToolbar。For example, the following code is used to define a UIToolbar in the UIKit namespace. 请注意,它是一个具有多个方法和属性的接口:Notice that it is an interface with a number of methods and properties:

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

    ...
}

在 Xamarin 中称为btouch的生成器使用这些定义文件,并使用 .net 工具将其编译为临时程序集The Generator, called btouch in Xamarin.iOS, takes these definition files and uses .NET tools to compile them into a temporary assembly. 但是,此临时程序集不能用于调用目标 C 代码。However, this temporary assembly is not useable to call Objective-C code. 然后,生成器将读取临时程序集, C#并生成可在运行时使用的代码。The generator then reads the temporary assembly and generates C# code that can be used at runtime. 例如,如果将随机属性添加到 .cs 文件中,则该属性不会显示在输出的代码中。This is why, for example, if you add a random attribute to your definition .cs file, it won’t show up in the outputted code. 生成器并不知道它,因此 btouch 不知道要将其输出到临时程序集中。The generator doesn’t know about it and therefore btouch doesn't know to look for it in the temporary assembly to output it.

创建 Xamarin .dll 后,mtouch 会将所有组件捆绑在一起。Once the Xamarin.iOS.dll has been created, mtouch will bundle all the components together.

从较高层次来看,通过执行以下任务实现此操作:At a high level, it achieves this by executing the following tasks:

  • 创建应用捆绑包结构。Create an app bundle structure.
  • 复制托管程序集中的。Copy in your managed assemblies.
  • 如果启用了链接,请运行托管链接器,以通过翻录未使用的部分来优化程序集。If linking is enabled, run the managed linker to optimize your assemblies by ripping unused parts out.
  • AOT 编译。AOT compilation.
  • 创建一个本机可执行文件,用于输出链接到本机可执行文件中的一系列静态库(每个程序集都有一个),以使本机可执行文件包含启动器代码、注册器代码(如果为静态)和来自 AOT 的所有输出编译程序Create a native executable, which outputs a series of static libraries (one for each assembly) that are linked into the native executable, so that the native executable consists of the launcher code, the registrar code (if static), and all the outputs from the AOT compiler

有关链接器及其使用方式的详细信息,请参阅链接器指南。For more detailed information on the linker and how it is used, refer to the Linker guide.

总结Summary

本指南探讨了 Xamarin iOS 应用的 AOT 编译,并深入探讨了与目标-C 之间的关系。This guide looked at AOT compilation of Xamarin.iOS apps, and explored Xamarin.iOS and its relationship to Objective-C in depth.