Xamarin.Mac 架構

本指南會探索 Xamarin.Mac 及其在低層級的關聯 Objective-C 性。 它會說明編譯、選取器、 registrars、應用程式啟動和產生器等概念。

概觀

Xamarin.Mac 應用程式會在 Mono 執行環境中執行,並使用 Xamarin 的編譯程式編譯成中繼語言 (IL),然後在運行時間將 Just-In-Time (JIT) 編譯成機器碼。 這會與運行時間並排執行 Objective-C 。 這兩個運行時間環境都會在類似 UNIX 的核心上執行,特別是 XNU,並將各種 API 公開給使用者程式代碼,讓開發人員存取基礎原生或受控系統。

下圖顯示此架構的基本概觀:

Diagram showing a basic overview of the architecture

原生和Managed程式碼

針對 Xamarin 進行開發時,通常會使用原生Managed 程式代碼等詞彙。 Managed 程式代碼是由 .NET Framework Common Language Runtime 所管理,或在 Xamarin 的案例中執行的程式代碼:Mono Runtime。

機器碼是在特定平臺上原生執行的程式代碼(例如, Objective-C 甚至是ARM晶片上的AOT編譯程式代碼)。 本指南會探索 Managed 程式代碼如何編譯成機器碼,並說明 Xamarin.Mac 應用程式的運作方式,透過系結充分利用 Apple 的 Mac API,同時也可以存取 。NET 的 BCL 和複雜的語言,例如 C#。

需求

使用 Xamarin.Mac 開發 macOS 應用程式需要下列項目:

  • 執行 macOS Sierra (10.12) 或更新的 Mac。
  • 最新版本的 Xcode (從 App Store 安裝)
  • 最新版的 Xamarin.Mac 和 Visual Studio for Mac

執行使用 Xamarin.Mac 建立的 Mac 應用程式有下列系統需求:

  • 執行 Mac OS X 10.7 或更高版本的 Mac。

編譯

當您編譯任何 Xamarin 平臺應用程式時,Mono C# (或 F#) 編譯程式將會執行,並將 C# 和 F# 程式代碼編譯成 Microsoft Intermediate Language (MSIL 或 IL)。 Xamarin.Mac 接著會在運行時間使用 Just in Time (JIT) 編譯程式來編譯機器碼,並視需要在正確的架構上執行。

這與使用 AOT 編譯的 Xamarin.iOS 相反。 使用 AOT 編譯程式時,所有元件和其中的所有方法都會在建置階段進行編譯。 使用 JIT 時,編譯只會視需要執行的方法進行。

使用 Xamarin.Mac 應用程式時,Mono 通常會內嵌到應用程式套件組合中(也稱為 Embedded Mono)。 使用傳統 Xamarin.Mac API 時,應用程式可能會改用 System Mono,不過,統一 API 不支援此功能。 系統Mono是指已在作業系統中安裝的Mono。 在應用程式啟動時,Xamarin.Mac 應用程式會使用此應用程式。

選取器

使用 Xamarin 時,我們有兩個不同的生態系統.NET 和 Apple,我們需要整合在一起,才能盡可能簡化,以確保最終目標是順暢的用戶體驗。 我們在上一節中看到這兩個運行時間的通訊方式,您可能非常瞭解「系結」一詞,這可讓原生 Mac API 用於 Xamarin。 系結會在系結檔中深入Objective-C說明,因此目前讓我們來探索 Xamarin.Mac 在幕後的運作方式。

首先,必須有一種方式可以公開 Objective-C 至 C#,這是透過 Selectors 完成的。 選取器是傳送至物件或類別的訊息。 透過Objective-Cobjc_msgSend函式來完成此作業。 如需使用選取器的詳細資訊,請參閱 iOS Objective-C 選取器 指南。 此外,還必須有一種方法,才能將Managed程式碼公開至 Objective-C,這更為複雜,因為 Objective-C 對Managed程式碼沒有任何瞭解。 為了解決這個問題,我們使用 registrar。 下一節會更詳細地說明這一點。

Registrar

如上所述, registrar 是將Managed程式代碼公開給 Objective-C的程序代碼。 其方式是建立衍生自 NSObject 的每個 Managed 類別列表:

  • 對於未包裝現有 Objective-C 類別的所有類別,它會建立新的 Objective-C 類別,其中 Objective-C 成員會鏡像具有 [Export] 屬性的所有 Managed 成員。
  • 在每個 Objective-C 成員的實作中,會自動新增程式代碼來呼叫鏡像受控成員。

下列虛擬程式代碼示範如何完成此作業的範例:

C# (Managed 程式代碼):

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 C# MyFunc method in MyViewController
}
@end

Managed 程式代碼可以包含 屬性, [Register] 以及 [Export], registrar 用來知道對象必須公開給 Objective-C。 [Register] 屬性是用來指定所 Objective-C 產生類別的名稱,以防預設產生的名稱不適用。 所有衍生自 NSObject 的類別都會自動向 Objective-C註冊。 必要的 [Export] 屬性包含字串,這是在產生的 Objective-C 類別中使用的選取器。

Xamarin.Mac 中有兩種類型的 registrars 使用 – 動態和靜態:

  • 動態 registrars – 這是所有 Xamarin.Mac 組建的預設值 registrar 。 動態 registrar 會在運行時間註冊元件中的所有類型。 它會使用 運行時間 API 所提供的 Objective-C函式來執行此作業。 因此,動態 registrar 的啟動速度較慢,但建置時間較快。 原生函式 (通常是在 C 中),稱為 trampolines,會在使用動態 registrars時做為方法實作。 它們在不同的架構之間有所不同。
  • Static – 靜態registrarsregistrar會在Objective-C建置期間產生程式代碼,然後編譯成靜態庫並連結至可執行檔。 這允許更快速的啟動,但在建置期間需要較長的時間。

應用程式啟動

Xamarin.Mac 啟動邏輯會根據使用內嵌或系統Mono而有所不同。 若要檢視 Xamarin.Mac 應用程式啟動的程式代碼和步驟,請參閱 xamarin-macios 公用存放庫中的啟動頭 檔。

Generator

Xamarin.Mac 包含每個 Mac API 的定義。 您可以在 MaciOS github 存放庫流覽其中任何一項。 這些定義包含具有屬性的介面,以及任何必要的方法和屬性。 例如,下列程式代碼是用來在AppKit命名空間定義NSBox。 請注意,它是具有許多方法和屬性的介面:

[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.Mac 中呼叫 bmac 的產生器會採用這些定義檔,並使用 .NET 工具來將它們編譯成暫存元件。 不過,這個暫存元件無法用來呼叫 Objective-C 程式代碼。 然後產生器會讀取暫存元件,併產生可在運行時間使用的 C# 程式代碼。 例如,如果您將隨機屬性新增至定義.cs檔案,它就不會顯示在輸出的程式代碼中。 產生器不知道它,因此 bmac 不知道在暫存元件中尋找它以輸出它。

建立Xamarin.Mac.dll之後,封裝程式 mmp會將所有元件組合在一起。

概括而言,它會藉由執行下列工作來達成此目的:

  • 建立應用程式套件組合結構。
  • 在受控元件中複製。
  • 如果已啟用連結,請執行 Managed 連結器,藉由移除未使用的元件來優化元件。
  • 建立啟動器應用程式,並在靜態模式中與程式代碼一起 registrar 連結啟動器程序代碼。

接著,這會在使用者建置程式中執行,將使用者程式代碼編譯成參考的元件Xamarin.Mac.dll並執行 mmp ,使其成為封裝

如需連結器及其使用方式的詳細資訊,請參閱 iOS 連結器 指南。

摘要

本指南探討 Xamarin.Mac 應用程式的編譯,並探索了 Xamarin.Mac 及其與 Objective-C的關聯性。