Xamarin 体系结构Xamarin.Mac architecture

本指南将在较低级别探索 Xamarin 和与其在目标-C 之间的关系。它介绍了一些概念,如编译、选择器、注册机构、应用程序启动和生成器。This guide explores Xamarin.Mac and its relationship to Objective-C at a low level. It explains concepts such as compilation, selectors, registrars, app launch, and the generator.

概述Overview

Xamarin 应用程序在 Mono 执行环境中运行,并使用 Xamarin 的编译器向下编译到中间语言(IL),然后在运行时实时(JIT)编译为本机代码。Xamarin.Mac applications run within the Mono execution environment, and use Xamarin’s compiler to compile down to Intermediate Language ( IL), which is then Just-in-Time ( JIT) compiled to native code at run-time. 这与目标 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:

显示体系结构基本概述的关系图Diagram showing a basic overview of the architecture

本机代码和托管代码Native and managed code

为 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.

本机代码是在特定平台(例如,目标平台,甚至是在 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). 本指南探讨了如何将托管代码编译为本机代码,并说明了 Xamarin 应用程序的工作原理,通过使用绑定,充分利用了 Apple 的 Mac Api,同时还具有对的访问权限。网络的 BCL 和复杂的语言(如C#)。This guide explores how your managed code is compiled to native code, and explains how a Xamarin.Mac application works, making full use of Apple’s Mac APIs through the use of bindings, while also having access to .NET’s BCL and a sophisticated language such as C#.

要求Requirements

以下是通过 Xamarin.Mac 开发 macOS 应用程序所需的条件:The following is required to develop a macOS application with Xamarin.Mac:

  • 运行 macOS Sierra (10.12)或更高版本的 Mac。A Mac running macOS Sierra (10.12) or greater.
  • 最新版本的 Xcode (从应用商店安装)The latest version of Xcode (installed from the App Store)
  • 最新版本的 Xamarin 和 Visual Studio for MacThe latest version of Xamarin.Mac and Visual Studio for Mac

运行通过 Xamarin.Mac 创建的 Mac 应用程序具有以下系统要求:Running Mac applications created with Xamarin.Mac have the following system requirements:

  • 运行 Mac OS X 10.7 或更高版本的 Mac。A Mac running Mac OS X 10.7 or greater.

编译Compilation

编译任何C# Xamarin 平台应用程序时,Mono (或F#)编译器将运行,并将C#和F#代码编译为 Microsoft 中间语言(MSIL 或 IL)。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 or IL). 然后,Xamarin 在运行时使用实时(JIT) 编译器来编译本机代码,从而允许根据需要在正确的体系结构上执行。Xamarin.Mac then uses a Just in Time (JIT) compiler at runtime to compile native code, allowing execution on the correct architecture as needed.

这与使用 AOT 编译的 Xamarin 不同。This is in contrast to Xamarin.iOS which uses AOT compilation. 使用 AOT 编译器时,将在生成时编译所有程序集和其中的所有方法。When using the AOT compiler, all assemblies and all methods within them are compiled at build time. 对于 JIT,只需对执行的方法进行编译。With JIT, compilation happens on demand only for the methods that are executed.

通过 Xamarin Mac 应用程序,Mono 通常嵌入到应用程序捆绑包(称为嵌入 Mono)。With Xamarin.Mac applications, Mono is usually embedded into the app bundle (and referred to as Embedded Mono). 使用经典 Xamarin Mac API 时,应用程序可以改用系统 Mono,不过,在 Unified API 中不支持这种情况。When using the Classic Xamarin.Mac API, the application could instead use System Mono, however, this is unsupported in the Unified API. 系统 Mono 指的是在操作系统中安装的 Mono。System Mono refers to Mono that has been installed in the operating system. 在应用程序启动时,Xamarin 应用将使用此。On application launch, the Xamarin.Mac app will use this.

选择器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 中使用本机 Mac 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 Mac APIs to be used in Xamarin. 目标-C 绑定文档中深入介绍了绑定,因此,我们现在来了解如何在幕后使用 Xamarin。Bindings are explained in depth in the Objective-C binding documentation, so for now, let’s explore how Xamarin.Mac 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. 有关使用选择器的详细信息,请参阅 iOS目标-C 选择器指南。For more information on using Selectors, refer to the iOS 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 a registrar. 下一节将对此进行更详细的介绍。This explained in more detail in the next section.

注册器Registrar

如上所述,注册机构是向目标-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 (native code):

@interface MyViewController : UIViewController
 - (void)myFunc;
@end 

@implementation MyViewController
- (void)myFunc {
    // Code to call the managed C# MyFunc method in MyViewController
}
@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.Mac – dynamic and static:

  • 动态注册机构–这是所有 Xamarin build 的默认注册机构。Dynamic registrars – This is the default registrar for all Xamarin.Mac builds. 动态注册器会在运行时注册程序集中的所有类型。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. 使用动态注册机构时,本机函数(通常为 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.

应用程序启动Application launch

Xamarin 启动逻辑将因使用嵌入的还是系统 Mono 而有所不同。Xamarin.Mac startup logic will differ depending on whether embedded or system Mono is used. 若要查看 Xamarin 应用程序启动的代码和步骤,请参阅 macios 公用存储库中的启动标头文件。To view the code and steps for Xamarin.Mac application launch, refer to the launch header file in the xamarin-macios public repo.

GeneratorGenerator

Xamarin 包含每个 Mac API 的定义。Xamarin.Mac contains definitions for every Mac 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. 例如,以下代码用于定义AppKit 命名空间中的 NSBox。For example, the following code is used to define an NSBox in the AppKit namespace. 请注意,它是一个具有多个方法和属性的接口:Notice that it is an interface with a number of methods and properties:

[BaseType (typeof (NSView))]
public interface NSBox {

    …

    [Export ("borderRect")]
    CGRect BorderRect { get; }

    [Export ("titleRect")]
    CGRect TitleRect { get; }

    [Export ("titleCell")]
    NSObject TitleCell { get; }

    [Export ("sizeToFit")]
    void SizeToFit ();

    [Export ("contentViewMargins")]
    CGSize ContentViewMargins { get; set; }

    [Export ("setFrameFromContentFrame:")]
    void SetFrameFromContentFrame (CGRect contentFrame);

    …

}

在 Xamarin 中称为 bmac 的生成器采用这些定义文件,并使用 .NET 工具将其编译为临时程序集。The Generator, called bmac in Xamarin.Mac, 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. 生成器并不知道这一点,因此 bmac 不知道要将其输出到临时程序集中。The generator doesn’t know about it, and therefore bmac doesn't know to look for it in the temporary assembly to output it.

创建 Xamarin 后,包装器 mmp会将所有组件捆绑在一起。Once the Xamarin.Mac.dll has been created, the packager, mmp, 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 removing unused parts.
  • 创建启动器应用程序,如果在静态模式下,则在启动器代码中进行链接将与注册器代码一起讨论。Create a launcher application, linking in the launcher code talked about along with the registrar code if in static mode.

然后,此操作将作为用户生成过程的一部分来运行,该过程将用户代码编译为一个程序集,该程序集引用的 Xamarin .dll 并运行 mmp 以使其成为包This is then run as part of the user build process that compiles user code into an assembly that reference's Xamarin.Mac.dll and runs mmp to make it a package

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

总结Summary

本指南介绍了 Xamarin. Mac 应用程序的编译,并探讨了如何将 Xamarin 与目标 C 进行了关联。This guide looked at compilation of Xamarin.Mac apps, and explored Xamarin.Mac and its relationship to Objective-C.