Xamarin.Mac 的運作方式
不過,開發人員在大部分時間都不需要擔心 Xamarin.Mac 的內部「魔術」,不過,對幕後運作方式有粗略的瞭解,將有助於在出現 C# 鏡頭和偵錯問題來解譯現有檔。
在 Xamarin.Mac 中,應用程式會橋接兩個世界:有 Objective-C 基底運行時間包含原生類別實例 (NSString
、 NSApplication
等),而且有 C# 執行時間包含 Managed 類別實例 (System.String
、 HttpClient
等) 的實例。 在這兩個世界之間,Xamarin.Mac 會建立雙向網橋,讓應用程式可以在 (例如) 中 Objective-C 呼叫方法 (例如 NSApplication.Init
), 並 Objective-C 可以呼叫應用程式的 C# 方法(例如應用程式委派上的方法)。 一般而言,對的呼叫 Objective-C 會透過 P/Invokes 以透明方式處理,而某些運行時間程式代碼 Xamarin 會提供。
將 C# 類別/方法公開至 Objective-C
不過, Objective-C 若要回呼應用程式的 C# 對象,必須以可以理解的方式 Objective-C 公開這些物件。 這是透過 Register
和 Export
屬性來完成。 以下列範例為例:
[Register ("MyClass")]
public class MyClass : NSObject
{
[Export ("init")]
public MyClass ()
{
}
[Export ("run")]
public void Run ()
{
}
}
在此範例中,運行時間Objective-C現在會知道名為 和的選取器所init
run
呼叫MyClass
的類別。
在大部分情況下,這是開發人員可以忽略的實作詳細數據,因為應用程式收到的大部分回呼都是透過類別的覆寫方法 base
(例如 AppDelegate
、 Delegates
DataSources
、 ) 或傳遞至 API 的動作。 在所有情況下, Export
C# 程式代碼中不需要屬性。
建構函式執行
在許多情況下,開發人員必須將應用程式的 C# 類別建構 API 公開至 Objective-C 運行時間,以便從 Storyboard 或 XIB 檔案中呼叫時等位置具現化。 以下是 Xamarin.Mac 應用程式中最常使用的五個建構函式:
// Called when created from unmanaged code
public CustomView (IntPtr handle) : base (handle)
{
Initialize ();
}
// Called when created directly from a XIB file
[Export ("initWithCoder:")]
public CustomView (NSCoder coder) : base (coder)
{
Initialize ();
}
// Called from C# to instance NSView with a Frame (initWithFrame)
public CustomView (CGRect frame) : base (frame)
{
}
// Called from C# to instance NSView without setting the frame (init)
public CustomView () : base ()
{
}
// This is a special case constructor that you call on a derived class when the derived called has an [Export] constructor.
// For example, if you call init on NSString then you don’t want to call init on NSObject.
public CustomView () : base (NSObjectFlag.Empty)
{
}
一般而言,開發人員應該保留 IntPtr
建立某些類型時所產生的 和 NSCoder
建構函式,例如單獨自定義 NSViews
。 如果 Xamarin.Mac 需要呼叫其中一個 Objective-C 建構函式來回應運行時間要求,而且您已將其移除,則應用程式會在機器碼內當機,而且可能很難確切找出問題。
記憶體管理和週期
Xamarin.Mac 中的記憶體管理在許多方面與 Xamarin.iOS 非常類似。 這也是一個複雜的主題,超出本檔的範圍。 請閱讀 記憶體和效能最佳做法。
預先編譯
一般而言,.NET 應用程式不會在建置時編譯成機器程序代碼,而是編譯成稱為 IL 程式代碼的中繼層,在應用程式啟動時,會取得 編譯成機器程式代碼的 Just-In-Time (JIT)。
將 Mono 執行時間用於 JIT 編譯此機器程式代碼的時間,可能會使啟動 Xamarin.Mac 應用程式的速度變慢 20%,因為需要時間才能產生必要的機器程式代碼。
由於 Apple 在 iOS 上施加的限制,Xamarin.iOS 無法使用 IL 程式代碼的 JIT 編譯。 因此,所有 Xamarin.iOS 應用程式都是 在建置週期期間編譯成機器程式代碼的完整預先 階段 (AOT)。
Xamarin.Mac 的新功能是可在應用程式建置週期期間對 IL 程式代碼進行 AOT,就像 Xamarin.iOS 可以一樣。 Xamarin.Mac 使用 混合式 AOT 方法來編譯大部分所需的機器程序代碼,但允許運行時間編譯所需的蹦床,以及繼續支援 Reflection.Emit 的彈性(以及目前在 Xamarin.Mac 上運作的其他使用案例)。
AOT 可協助 Xamarin.Mac 應用程式的主要區域有兩個主要區域:
- 更好的「原生」損毀記錄 - 如果 Xamarin.Mac 應用程式在原生程式代碼中當機,在對 Cocoa API 進行無效呼叫時,通常會發生這種狀況(例如將 傳送
null
至不接受它的方法),使用 JIT 框架的原生損毀記錄很難分析。 由於 JIT 畫面格沒有偵錯資訊,因此會有多行十六進位移,而且不知道發生了什麼事。 AOT 會產生「真實」的具名畫面格,而且追蹤更容易閱讀。 這也表示 Xamarin.Mac 應用程式會與 lldb 和 Instruments 等原生工具互動得更好。 - 更好的啟動時間效能 - 對於大型 Xamarin.Mac 應用程式,使用多次啟動時間,JIT 編譯所有程式代碼可能需要相當長的時間。 AOT 會預先執行此工作。
啟用 AOT 編譯
在 Xamarin.Mac 中啟用 AOT,方法是按兩下 方案總管 中的 [項目名稱],流覽至 [Mac 組建] 並新增--aot:[options]
至 [其他 mmp 自變數:] 欄位(其中[options]
是一或多個控制 AOT 類型的選項,請參閱下方)。 例如:
重要
啟用 AOT 編譯可大幅增加建置時間,有時長達數分鐘,但平均可改善應用程式啟動時間 20%。 因此,AOT 編譯應該只在 Xamarin.Mac 應用程式的發行組建上啟用。
Aot 編譯選項
在 Xamarin.Mac 應用程式上啟用 AOT 編譯時,有數種不同的選項可以調整:
none
- 沒有 AOT 編譯。 這是預設設定。all
- AOT 會編譯MonoBundle中的每個元件。core
- AOT 會Xamarin.Mac
編譯、System
和mscorlib
元件。sdk
- AOT 會Xamarin.Mac
編譯 和基類庫 (BCL) 元件。|hybrid
- 將此新增至上述其中一個選項會啟用混合式 AOT,以允許 IL 等量分割,但會導致編譯時間較長。+
- 包含 AOT 編譯的單一檔案。-
- 從 AOT 編譯中移除單一檔案。
例如,會在MonoBundle中的所有元件上啟用AOT編譯,--aot:all,-MyAssembly.dll
但 MyAssembly.dll
會--aot:core|hybrid,+MyOtherAssembly.dll,-mscorlib.dll
啟用混合式,程式代碼 AOT 會包含 MyOtherAssembly.dll
和排除 mscorlib.dll
。
部分靜態 registrar
開發 Xamarin.Mac 應用程式時,將完成變更和測試之間的時間降到最低,對於符合開發期限而言非常重要。 程式代碼基底和單元測試模組化等策略有助於減少編譯時間,因為它們可減少應用程式需要大量完整重建的次數。
此外,Xamarin.Mac 的新功能部分靜態Registrar(如 Xamarin.iOS 所先導)可以大幅減少偵錯組態中 Xamarin.Mac 應用程式的啟動時間。 瞭解使用部分靜態 Registrar 如何壓縮偵錯啟動的近 5 倍改進,將需要一些背景,了解什麼是 registrar 靜態和動態,以及這個「部分靜態」版本有何差異。
關於 registrar
任何 Xamarin.Mac 應用程式都位於 Apple 和運行時間的 Objective-C Cocoa 架構下。 在這個「原生世界」與 C# 的「受控世界」之間建立橋樑是 Xamarin.Mac 的主要責任。 這項工作的一部分是由 registrar,在方法內 NSApplication.Init ()
執行。 這是在 Xamarin.Mac 中使用 Cocoa API 時需要 NSApplication.Init
先呼叫的一個原因。
registrar的工作是通知Objective-C運行時間應用程式 C# 類別是否存在,這些類別衍生自 、、 NSView
NSWindow
和 NSObject
等NSApplicationDelegate
類別。 這需要掃描應用程式中的所有類型,以判斷需要註冊的專案,以及要報告之每個類型上的專案。
此掃描可以在應用程式啟動時以動態方式進行,以反映或靜態方式執行,做為建置時間步驟。 挑選註冊類型時,開發人員應該注意下列事項:
- 靜態註冊可以大幅減少啟動時間,但可能會大幅降低建置時間(通常比偵錯建置時間增加一倍以上)。 這會是發行組態組建的預設值。
- 動態註冊會延遲這項工作,直到應用程式啟動並略過程式代碼產生,但此額外工作可以在應用程式啟動中建立明顯的暫停(至少兩秒)。 這在偵錯組態組建中特別明顯,預設為動態註冊,且反映速度較慢。
部分靜態註冊,首先在 Xamarin.iOS 8.13 中引進,可讓開發人員充分利用這兩個選項。 藉由在靜態庫中預先計算每個元素 Xamarin.Mac.dll
的註冊資訊,並將此資訊與 Xamarin.Mac 一起傳送至靜態庫(只需要在建置時間連結),Microsoft已移除動態 registrar 的大部分反映時間,同時不會影響建置時間。
啟用部分靜態 registrar
在 Xamarin.Mac 中啟用部分靜態Registrar,方法是按兩下 方案總管 中的 [項目名稱],流覽至 [Mac 組建] 並新增--registrar:static
至 [其他 mmp 自變數:] 欄位。 例如:
其他資源
以下是內部運作方式的一些更詳細的說明: