共用方式為


Xamarin.iOS API 設計

除了屬於Mono的核心基類連結庫之外, Xamarin.iOS 隨附各種iOS API的系結,可讓開發人員使用Mono建立原生iOS 應用程式。

在 Xamarin.iOS 的核心,有一個 Interop 引擎會與 Objective-C 世界橋接 C# 世界,以及 iOS C 型 API 的系結,例如 CoreGraphics 和 OpenGL ES

與 Objective-C 程式代碼通訊的低階運行時間位於 MonoTouch.ObjCRuntime中。 此外,會提供 Foundation、CoreFoundation 和 UIKit 的系結。

設計原則

本節詳細說明 Xamarin.iOS 系結的一些設計原則(也適用於 Xamarin.Mac,macOS 上的 Mono 系 Objective-C 結):

  • 遵循架構設計指導方針

  • 允許開發人員使用子類別 Objective-C :

    • 衍生自現有的類別
    • 呼叫基底建構函式以鏈結
    • 覆寫方法應該使用 C# 的覆寫系統來完成
    • 子類別處理應該使用 C# 標準建構
  • 請勿向 Objective-C 選取器公開開發人員

  • 提供呼叫任意 Objective-C 連結庫的機制

  • 讓一般Objective-C工作變得簡單而困難Objective-C

  • 將屬性公開 Objective-C 為 C# 屬性

  • 公開強型別 API:

    • 提高類型安全性
    • 將運行時間錯誤降至最低
    • 取得傳回類型的 IDE IntelliSense
    • 允許 IDE 快顯檔
  • 鼓勵 IDE 內探索 API:

    • 例如,而不是公開弱型別陣列,如下所示:

      NSArray *getViews
      

      公開強型別,如下所示:

      NSView [] Views { get; set; }
      

      使用強型別可讓 Visual Studio for Mac 在瀏覽 API 時執行自動完成、讓傳回值上提供所有 System.Array 作業,並允許傳回值參與 LINQ。

  • 原生 C# 類型:

    • NSString 變成 string

    • uint將應該列舉的參數轉換成 int C# 列舉和具有[Flags]屬性的 C# 列舉

    • 而不是類型中性 NSArray 物件,會將數位公開為強型別數位。

    • 針對事件和通知,可讓用戶選擇:

      • 默認為強型別版本
      • 進階使用案例的弱型別版本
  • Objective-C支援委派模式:

    • C# 事件系統
    • 將 C# 委派 (Lambda、匿名方法和 System.Delegate) 公開為 Objective-C 區塊的 API

組件

Xamarin.iOS 包含許多構成 Xamarin.iOS 配置檔元件。 [ 元件] 頁面有詳細資訊。

主要命名空間

ObjCRuntime

ObjCRuntime 命名空間可讓開發人員在 C# 與 Objective-C之間橋接世界。 這是一個新的系結,專為 iOS 設計,根據 Cocoa# 和 Gtk# 的體驗。

Foundation

Foundation 命名空間提供基本數據類型,其設計目的是要與Objective-C屬於iOS一部分的Foundation架構互操作,而且它是 中Objective-C面向物件程式設計的基礎。

C# 中來自 類別階層的 Objective-CXamarin.iOS 鏡像。 例如,Objective-C基類 NSObject 可透過 Foundation.NSObjectC# 使用。

雖然 Foundation 命名空間提供基礎 Objective-C 基礎基礎型別的系結,但在少數情況下,我們已將基礎型別對應至 .NET 類型。 例如:

  • 運行時間不是處理 NSString 和 NSArray,而是在整個 API 中將其公開為 C# 字串和強型別數位。

  • 這裡公開各種協助程式 API,可讓開發人員系結第三方 Objective-C API、其他 iOS API 或目前未受 Xamarin.iOS 系結的 API。

如需系結 API 的詳細資訊,請參閱 Xamarin.iOS 系結產生器 一節。

NSObject

NSObject 類型是所有Objective-C系結的基礎。 Xamarin.iOS 類型會鏡像 iOS CocoaTouch API 中的兩個類型類別:C 類型(通常稱為 CoreFoundation 類型)和 Objective-C 類型(全都衍生自 NSObject 類別)。

針對鏡像 Unmanaged 類型的每個類型,可以透過 Handle 屬性取得原生物件。

雖然 Mono 會為所有物件提供垃圾收集,但會 Foundation.NSObject作 System.IDisposable 介面。 您可以明確釋放任何指定 NSObject 的資源,而不需要等待垃圾收集行程開始。 當您使用大量 NSObjects 時,明確釋放資源很重要,例如,可能保存大型數據區塊指標的 UIImages。

如果您的類型需要執行確定性最終處理,請覆寫 NSObject.Dispose(bool) 方法 Dispose 的參數是 “bool disposing”,如果設定為 true,表示您的 Dispose 方法 正在呼叫,因為使用者明確在物件上呼叫 Dispose () 。 false 值表示正在從完成項線程上的完成項呼叫 Dispose(bool disposing) 方法。

類別

從 Xamarin.iOS 8.10 開始,您可以從 C# 建立 Objective-C 類別。

這是使用 屬性完成的 Category ,指定要擴充為 屬性自變數的類型。 下列範例會針對 實例擴充 NSString。

[Category (typeof (NSString))]

每個類別方法都會使用一般機制來導出方法,以 Objective-C 使用 Export 屬性:

[Export ("today")]
public static string Today ()
{
    return "Today";
}

所有 Managed 擴充方法都必須是靜態的,但可以使用 C# 中擴充方法的標準語法來建立 Objective-C 實例方法:

[Export ("toUpper")]
public static string ToUpper (this NSString self)
{
    return self.ToString ().ToUpper ();
}

和擴充方法的第一個自變數將會是叫用方法的 實例。

完整範例:

[Category (typeof (NSString))]
public static class MyStringCategory
{
    [Export ("toUpper")]
    static string ToUpper (this NSString self)
    {
        return self.ToString ().ToUpper ();
    }
}

此範例會將原生 toUpper 實例方法新增至 NSString 類別,可從 叫 Objective-C用。

[Category (typeof (UIViewController))]
public static class MyViewControllerCategory
{
    [Export ("shouldAutoRotate")]
    static bool GlobalRotate ()
    {
        return true;
    }
}

其中一個很有用的案例是將方法新增至程式代碼基底中的一組類別,例如,這會讓所有 UIViewController 實例報告輪替:

[Category (typeof (UINavigationController))]
class Rotation_IOS6 {
      [Export ("shouldAutorotate:")]
      static bool ShouldAutoRotate (this UINavigationController self)
      {
          return true;
      }
}
PreserveAttribute

PreserveAttribute 是自定義屬性,用來告訴 mtouch – Xamarin.iOS 部署工具 – 在處理應用程式以減少其大小時,保留類型或類型的成員。

未由應用程式以靜態方式連結的每個成員都可能會移除。 因此,這個屬性可用來標記不是靜態參考的成員,但應用程式仍需要這個成員。

例如,若您是以動態方式來具現化類型,您可以保留您類型的預設建構函式。 若使用 XML 序列化,您可能會想要保留您類型的屬性。

您可以將此屬性套用到某個類型的每個成員,或是套用到類型本身。 如果您想要保留整個類型,您可以在類型上使用語法 [Preserve (AllMembers = true)]。

UIKit

UIKit 命名空間包含一對一對應,以 C# 類別形式組成 CocoaTouch 的所有 UI 元件。 API 已修改為遵循 C# 語言中使用的慣例。

C# 委派會針對一般作業提供。 如需詳細資訊,請參閱 委派一 節。

OpenGLES

對於 OpenGLES,我們會散發 已修改 版本的 OpenTK API、已修改為使用 CoreGraphics 數據類型和結構的 OpenGL 物件導向系結,並且只會公開 iOS 上可用的功能。

OpenGLES 1.1 功能可透過 ES11.GL 類型取得。

OpenGLES 2.0 功能可透過 ES20.GL 類型取得。

OpenGLES 3.0 功能可透過 ES30.GL 類型取得。

系結設計

Xamarin.iOS 不只是基礎 Objective-C 平臺的系結。 它會擴充 .NET 類型系統並分派系統,以更好地混合 C# 和 Objective-C。

就像 P/Invoke 是一個有用的工具,可在 Windows 和 Linux 上叫用原生連結庫,或作為 IJW 支援,可用於 Windows 上的 COM Interop,Xamarin.iOS 會擴充運行時間以支援將 C# 物件系結至 Objective-C 物件。

建立 Xamarin.iOS 應用程式的使用者不需要後續幾個小節的討論,但可協助開發人員瞭解如何完成工作,並在建立更複雜的應用程式時協助他們。

類型

在合理的情況下,C# 類型會公開至 C# 宇宙,而不是低階基礎類型。 這表示 API 會使用 C# “string” 類型,而不是 NSString ,而是使用強型別 C# 陣列,而不是公開 NSArray。

一般而言,在 Xamarin.iOS 和 Xamarin.Mac 設計中,基礎 NSArray 物件不會公開。 相反地,運行時間會自動將 轉換成 NSArray某些 NSObject 類別的強型別數位列。 因此,Xamarin.iOS 不會公開像 GetViews 這樣的弱型別方法,以傳回 NSArray:

NSArray GetViews ();

相反地,系結會公開強型別傳回值,如下所示:

UIView [] GetViews ();

在 中 NSArray公開了幾個方法,針對您可能想要直接使用的 NSArray 角落案例,但 API 系結中不建議使用它們。

此外,在 傳統 API 中,我們不會從 CoreGraphics API 公開 CGRectCGPointCGSize ,而是將它們取代為 System.DrawingRectangleF作 、 PointFSizeF ,因為它們可協助開發人員保留使用 OpenTK 的現有 OpenGL 程式代碼。 使用新的 64 位 整合 API 時,應該使用 CoreGraphics API。

繼承

Xamarin.iOS API 設計可讓開發人員以擴充 C# 類型的相同方式擴充原生 Objective-C 類型、在衍生類別上使用 “override” 關鍵詞,並使用 “base” C# 關鍵詞鏈結至基底實作。

此設計可讓開發人員避免在開發過程中處理 Objective-C 選取器,因為整個 Objective-C 系統已經包裝在 Xamarin.iOS 連結庫中。

類型和介面產生器

當您建立屬於 Interface Builder 所建立類型的實例的 .NET 類別時,您必須提供採用單 IntPtr 一參數的建構函式。 這需要系結受控物件實例與 Unmanaged 物件。 程序代碼是由單行所組成,如下所示:

public partial class void MyView : UIView {
   // This is the constructor that you need to add.
   public MyView (IntPtr handle) : base (handle) {}
}

委派

Objective-C 和 C# 對於每個語言中的委派字組有不同的意義。

Objective-C在世界上,以及您會在在線找到CocoaTouch的檔中,委派通常是類別的實例,其會回應一組方法。 這類似於 C# 介面,其差異在於方法不一定是強制性的。

這些委派在UIKit和其他CocoaTouch API中扮演重要角色。 它們可用來完成各種工作:

  • 為程式代碼提供通知(類似於 C# 或 Gtk+ 中的事件傳遞)。
  • 實作數據視覺效果控件的模型。
  • 驅動控件的行為。

程序設計模式旨在將衍生類別的建立降到最低,以改變控件的行為。 此解決方案的精神與多年來其他 GUI 工具組所做的類似:Gtk 的訊號、Qt 插槽、Winforms 事件、WPF/Silverlight 事件等等。 若要避免有數百個介面(每個動作各有一個),或要求開發人員實作不需要的太多方法, Objective-C 支援選擇性的方法定義。 這與需要實作所有方法的 C# 介面不同。

在 Objective-C 類別中,您會看到使用此程序設計模式的類別公開屬性,稱為 delegate,這是實作介面的必要部分,以及選擇性元件零或更多部分的必要部分。

在 Xamarin.iOS 中,會提供三個互斥機制來系結至這些委派:

  1. 透過事件
  2. 透過 Delegate 屬性強型別
  3. 透過 WeakDelegate 屬性鬆散類型

例如,請考慮UIWebView類別。 這會分派至指派給委派屬性的 UIWebViewDelegate 實例。

透過事件

對於許多類型,Xamarin.iOS 會自動建立適當的委派,以將呼叫轉送 UIWebViewDelegate 至 C# 事件。 針對 UIWebView

例如,這個簡單的程式會記錄載入 Web 檢視時的開始和結束時間:

DateTime startTime, endTime;
var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.LoadStarted += (o, e) => startTime = DateTime.Now;
web.LoadFinished += (o, e) => endTime = DateTime.Now;
透過屬性

當事件可能有一個以上的訂閱者時,事件會很有用。 此外,事件僅限於程式代碼中沒有傳回值的情況。

如果程式代碼預期會傳回值,我們改為選擇屬性。 這表示物件中的指定時間只能設定一個方法。

例如,您可以使用這個機制來關閉 處理程式之 畫面 UITextField上的鍵盤:

void SetupTextField (UITextField tf)
{
    tf.ShouldReturn = delegate (textfield) {
        textfield.ResignFirstResponder ();
        return true;
    }
}

UITextField在此情況下,的 ShouldReturn 屬性會以傳回bool值的委派做為自變數,並判斷TextField是否應該在按下 Return 按鈕時執行動作。 在我們的 方法中,我們會將 true 傳回給呼叫者,但我們也會從畫面移除鍵盤(這發生在文字字段呼叫 ResignFirstResponder時)。

透過委派屬性強型別

如果您不想使用事件,您可以提供自己的 UIWebViewDelegate 子類別,並將它指派給 UIWebView.Delegate 屬性。 指派 UIWebView.Delegate 之後,UIWebView 事件分派機制將無法再運作,而且在發生對應的事件時會叫用 UIWebViewDelegate 方法。

例如,這個簡單類型會記錄載入 Web 檢視所需的時間:

class Notifier : UIWebViewDelegate  {
    DateTime startTime, endTime;

    public override LoadStarted (UIWebView webview)
    {
        startTime = DateTime.Now;
    }

    public override LoadingFinished (UIWebView webView)
    {
        endTime= DateTime.Now;
    }
}

上述用於程式代碼中,如下所示:

var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.Delegate = new Notifier ();

上述會建立UIWebViewer,並指示其將訊息傳送至 Notifier 實例,這是為了回應訊息而建立的類別。

此模式也可用來控制特定控件的行為,例如在UIWebView案例中, UIWebView.ShouldStartLoad 屬性可讓 UIWebView 實例控制 是否會 UIWebView 載入頁面。

此模式也可用來視需要提供一些控件的數據。 例如, UITableView 控件是功能強大的數據表轉譯控件,而且外觀和內容都是由UITableViewDataSource的 實例所驅動

透過 WeakDelegate 屬性以鬆散方式輸入

除了強型別屬性之外,還有弱型別委派,可讓開發人員視需要以不同的方式系結專案。 在 Xamarin.iOS 系結中公開強型 Delegate 別屬性時,也會公開對應的 WeakDelegate 屬性。

使用 WeakDelegate時,您必須負責使用 Export 屬性正確裝飾類別,以指定選取器。 例如:

class Notifier : NSObject  {
    DateTime startTime, endTime;

    [Export ("webViewDidStartLoad:")]
    public void LoadStarted (UIWebView webview)
    {
        startTime = DateTime.Now;
    }

    [Export ("webViewDidFinishLoad:")]
    public void LoadingFinished (UIWebView webView)
    {
        endTime= DateTime.Now;
    }
}

[...]

var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.WeakDelegate = new Notifier ();

WeakDelegate指派屬性之後,Delegate將不會使用 屬性。 此外,如果您在想要 [Export] 的繼承基類中實作 方法,則必須將其設為公用方法。

將 Objective-C 委派模式對應至 C#

當您看到 Objective-C 如下所示的範例時:

foo.delegate = [[SomethingDelegate] alloc] init]

這會指示語言建立及建構類別 「SomethingDelegate」 的實例,並將值指派給 foo 變數上的委派屬性。 Xamarin.iOS 支援此機制,而 C# 語法為:

foo.Delegate = new SomethingDelegate ();

在 Xamarin.iOS 中,我們提供了對應至委派類別的 Objective-C 強型別類別。 若要使用它們,您將進行子類別化,並覆寫 Xamarin.iOS 實作所定義的方法。 如需其運作方式的詳細資訊,請參閱下面的一節。

將委派對應至 C#

一般而言,UIKit 會使用 Objective-C 兩種形式的委派。

第一個窗體會提供元件模型的介面。 例如,作為依需求提供檢視數據的機制,例如清單檢視的數據儲存設施。 在這些情況下,您應該一律建立適當類別的實例,並指派變數。

在下列範例中,我們會為使用字串的模型提供 UIPickerView 實作:

public class SampleTitleModel : UIPickerViewTitleModel {

    public override string TitleForRow (UIPickerView picker, nint row, nint component)
    {
        return String.Format ("At {0} {1}", row, component);
    }
}

[...]

pickerView.Model = new MyPickerModel ();

第二個表單是提供事件的通知。 在這些情況下,雖然我們仍然以上述形式公開 API,但我們也會提供 C# 事件,這應該更容易用於快速作業,並在 C# 中與匿名委派和 Lambda 表達式整合。

例如,您可以訂閱 UIAccelerometer 事件:

UIAccelerometer.SharedAccelerometer.Acceleration += (sender, args) => {
   UIAcceleration acc = args.Acceleration;
   Console.WriteLine ("Time={0} at {1},{2},{3}", acc.Time, acc.X, acc.Y, acc.Z);
}

這兩個選項在合理之處可供使用,但身為程式設計人員,您必須挑選一個或另一個選項。 如果您建立自己的強型別回應程式/委派實例並加以指派,C# 事件將無法運作。 如果您使用 C# 事件,則永遠不會呼叫響應程式/委派類別中的方法。

先前使用的 UIWebView 範例可以使用 C# 3.0 Lambda 來撰寫,如下所示:

var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.LoadStarted += () => { startTime = DateTime.Now; }
web.LoadFinished += () => { endTime = DateTime.Now; }

回應事件

在程式代碼中 Objective-C ,多個控件的事件處理程式和多個控件的資訊提供者有時會裝載在相同的類別中。 這是可能的,因為類別會回應訊息,而且只要類別回應訊息,就可以將對象連結在一起。

如先前所述,Xamarin.iOS 同時支援 C# 事件型程序設計模型和 Objective-C 委派模式,您可以在其中建立實作委派並覆寫所需方法的新類別。

您也可以支援 Objective-C的模式,讓多個不同作業的回應者全都裝載在類別的相同實例中。 不過,若要這樣做,您必須使用 Xamarin.iOS 系結的低階功能。

例如,如果您想要讓類別同時回應 UITextFieldDelegate.textFieldShouldClear: 訊息和 UIWebViewDelegate.webViewDidStartLoad類別中的 : ,則必須使用 [Export] 屬性宣告:

public class MyCallbacks : NSObject {
    [Export ("textFieldShouldClear:"]
    public bool should_we_clear (UITextField tf)
    {
        return true;
    }

    [Export ("webViewDidStartLoad:")]
    public void OnWebViewStart (UIWebView view)
    {
        Console.WriteLine ("Loading started");
    }
}

方法的 C# 名稱並不重要;所有重要的是傳遞至 [Export] 屬性的字串。

使用此程式設計樣式時,請確定 C# 參數符合執行時間引擎將傳遞的實際類型。

模型

在UIKit儲存設施中,或在使用協助程式類別實作的回應程式中,這些會以委派的形式在程式碼中 Objective-C 參考,並實作為通訊協定。

Objective-C 通訊協定就像介面,但它們支持選擇性方法,也就是說,並非所有方法都需要實作,通訊協定才能運作。

實作模型的方法有兩種。 您可以手動實作它,或使用現有的強型別定義。

當您嘗試實作尚未受 Xamarin.iOS 系結的類別時,需要手動機制。 很容易做到:

  • 為類別加上旗標以向運行時間註冊
  • 在您想要覆寫的每個方法上,套用具有實際選取器名稱的 [Export] 屬性
  • 具現化 類別,並傳遞它。

例如,下列方法只會在UIApplicationDelegate通訊協定定義中實作其中一個選擇性方法:

public class MyAppController : NSObject {
        [Export ("applicationDidFinishLaunching:")]
        public void FinishedLaunching (UIApplication app)
        {
                SetupWindow ();
        }
}

選取 Objective-C 器名稱 (“applicationDidFinishLaunching:”) 是以 Export 屬性宣告,而且類別會向 屬性註冊 [Register]

Xamarin.iOS 提供強型別宣告,可供使用,不需要手動系結。 為了支援此程序設計模型,Xamarin.iOS 執行時間支援類別宣告上的 [Model] 屬性。 這會通知運行時間,除非明確實作方法,否則它不應該連接 類別中的所有方法。

這表示在 UIKit 中,以選擇性方法表示通訊協定的類別會像這樣撰寫:

[Model]
public class SomeViewModel : NSObject {
    [Export ("someMethod:")]
    public virtual int SomeMethod (TheView view) {
       throw new ModelNotImplementedException ();
    }
    ...
}

當您想要實作只實作某些方法的模型時,您只需要覆寫您感興趣的方法,並忽略其他方法。 運行時間只會連結覆寫的方法,而不是將原始方法連結至 Objective-C 世界。

相當於上一個手動範例的為:

public class AppController : UIApplicationDelegate {
    public override void FinishedLaunching (UIApplication uia)
    {
     ...
    }
}

優點是不需要深入探索 Objective-C 頭檔來尋找選取器、自變數的類型或 C# 的對應,以及從 Visual Studio for Mac 取得 Intellisense,以及強型別

XIB 出口和 C#

這是一個低階描述,說明輸出如何與 C# 整合,並為 Xamarin.iOS 的進階使用者提供。 使用 Visual Studio for Mac 時,對應會在幕後自動為您使用正式發行前小眾測試版上產生的程式代碼。

當您使用 Interface Builder 設計使用者介面時,您只會設計應用程式的外觀,並建立一些預設連線。 如果您想要以程式設計方式擷取資訊,請在運行時間改變控件的行為或修改控件,則必須將部分控件系結至您的Managed程式代碼。

這會在幾個步驟中完成:

  1. 輸出宣告 新增至檔案 的擁有者
  2. 將控件 連線 至檔案的擁有者
  3. 將 UI 加上連線儲存到您的 XIB/NIB 檔案。
  4. 在運行時間載入 NIB 檔案。
  5. 存取輸出變數。

步驟 (1) 到 (3) 涵蓋在 Apple 的檔中,以建置與 Interface Builder 的介面。

使用 Xamarin.iOS 時,您的應用程式必須建立衍生自 UIViewController 的類別。 其實作方式如下:

public class MyViewController : UIViewController {
    public MyViewController (string nibName, NSBundle bundle) : base (nibName, bundle)
    {
        // You can have as many arguments as you want, but you need to call
        // the base constructor with the provided nibName and bundle.
    }
}

然後,若要從 NIB 檔案載入 ViewController,請執行此動作:

var controller = new MyViewController ("HelloWorld", NSBundle.MainBundle, this);

這會從 NIB 載入使用者介面。 現在,若要存取輸出,必須通知運行時間我們想要存取它們。 若要這樣做,UIViewController子類別必須宣告屬性,並使用 [連線] 屬性為其加上批注。 與下列類似:

[Connect]
UITextField UserName {
    get {
        return (UITextField) GetNativeField ("UserName");
    }
    set {
        SetNativeField ("UserName", value);
    }
}

屬性實作是實際擷取並儲存實際原生類型的值。

使用 Visual Studio for Mac 和 InterfaceBuilder 時,您不需要擔心這個問題。 Visual Studio for Mac 會自動鏡像所有宣告的輸出,並在部分類別中編譯為專案的一部分程序代碼。

選取器

程序設計的核心概念 Objective-C 是選取器。 您通常會遇到需要傳遞選取器的 API,或預期您的程式代碼會回應選取器。

在 C# 建立新的選取器很簡單 – 您只要建立 類別的新實例 ObjCRuntime.Selector ,並在 API 中要求它的任何位置使用結果。 例如:

var selector_add = new Selector ("add:plus:");

對於 C# 方法回應選取器呼叫,它必須繼承自 NSObject 類型,而且必須使用 屬性以選取器名稱 [Export] 裝飾 C# 方法。 例如:

public class MyMath : NSObject {
    [Export ("add:plus:")]
    int Add (int first, int second)
    {
         return first + second;
    }
}

選取器名稱 必須 完全相符,包括所有中繼和尾端冒號(“:”),如果有的話。

NSObject 建構函式

衍生自 NSObject 的 Xamarin.iOS 中大部分類別都會公開物件功能特有的建構函式,但它們也會公開不明顯的各種建構函式。

建構函式的使用方式如下:

public Foo (IntPtr handle)

當運行時間需要將類別對應至 Unmanaged 類別時,這個建構函式會用來具現化類別。 當您載入 XIB/NIB 檔案時,就會發生這種情況。 此時,運行時間 Objective-C 會在 Unmanaged 世界中建立 物件,而且會呼叫此建構函式來初始化受控端。

一般而言,您只需要使用 handle 參數呼叫基底建構函式,並在主體中執行任何必要的初始化。

public Foo ()

這是類別的預設建構函式,而且在 Xamarin.iOS 提供的類別中,這會初始化 Foundation.NSObject 類別以及介於 和 結尾之間的所有類別,並將這個鏈結至 Objective-Cinit 類別上的 方法。

public Foo (NSObjectFlag x)

這個建構函式是用來初始化 實例,但會防止程式代碼在結尾呼叫 Objective-C 「init」 方法。 當您已經註冊初始化(當您 [Export] 在建構函式上使用時),或已經透過另一個平均值完成初始化時,通常會使用這個方法。

public Foo (NSCoder coder)

這個建構函式會針對從 NSCoding 實例初始化物件的案例提供。

例外狀況

Xamarin.iOS API 設計不會將例外狀況引發 Objective-C 為 C# 例外狀況。 設計會強制一開始不會將垃圾傳送到 Objective-C 世界,而且系結本身必須產生的任何例外狀況,才會將無效的數據傳遞至 Objective-C 世界。

通知

在 iOS 和 OS X 中,開發人員都可以訂閱基礎平台廣播的通知。 這是使用方法完成的 NSNotificationCenter.DefaultCenter.AddObserver 。 方法 AddObserver 會採用兩個參數;一個是您想要訂閱的通知;另一個是引發通知時要叫用的方法。

在 Xamarin.iOS 和 Xamarin.Mac 中,各種通知的密鑰會裝載在觸發通知的類別上。 例如,所 UIMenuController 引發的通知會裝載為 static NSString 類別中 UIMenuController 以 「Notification」 名稱結尾的屬性。

記憶體管理

Xamarin.iOS 有垃圾收集行程,會在資源不再使用時為您負責釋放資源。 除了垃圾收集行程之外,所有衍生自 NSObject 的對象都會實作 System.IDisposable 介面。

NSObject 和 IDisposable

IDisposable公開介面是一種方便的方式,可協助開發人員釋放可能封裝大量記憶體區塊的物件(例如,UIImage可能只是一個無辜的指標,但可能指向 2 MB 影像)和其他重要且有限的資源(例如視訊譯碼緩衝區)。

NSObject 會實作 IDisposable 介面,以及 .NET Dispose 模式。 這可讓子類別 NSObject 的開發人員覆寫 Dispose 行為,並視需要釋放自己的資源。 例如,假設此檢視控制器會保留一堆影像:

class MenuViewController : UIViewController {
    UIImage breakfast, lunch, dinner;
    [...]
    public override void Dispose (bool disposing)
    {
        if (disposing){
             if (breakfast != null) breakfast.Dispose (); breakfast = null;
             if (lunch != null) lunch.Dispose (); lunch = null;
             if (dinner != null) dinner.Dispose (); dinner = null;
        }
        base.Dispose (disposing)
    }
}

處置 Managed 物件時,不再有用。 您可能仍有對象的參考,但物件目前的所有意圖和目的無效。 如果您嘗試存取已處置物件上的任何方法,某些 .NET API 會擲回 ObjectDisposedException 來確保此目的,例如:

var image = UIImage.FromFile ("demo.png");
image.Dispose ();
image.XXX = false;  // this at this point is an invalid operation

即使您仍然可以存取變數 「image」,它確實是無效的參考,而且不再指向 Objective-C 保留影像的物件。

但是,在 C# 中處置物件並不表示物件一定會被終結。 您所做的就是釋放 C# 對 對物件所擁有的參考。 Cocoa 環境可能為了自己的使用而保留參考。 例如,如果您將UIImageView的Image屬性設定為影像,然後處置影像,則基礎UIImageView已取得自己的參考,並且會保留此對象的參考,直到使用該物件完成為止。

呼叫 Dispose 的時機

當您需要Mono來擺脫物件時,請呼叫 Dispose。 可能的使用案例是當Mono不知道您的NSObject 實際上是持有記憶體或資訊集區等重要資源的參考時。 在這些情況下,您應該呼叫 Dispose 立即釋放記憶體的參考,而不是等候 Mono 執行垃圾收集迴圈。

就內部來說,當Mono從 C# 字串建立 NSString 參考時,它會立即處置它們,以減少垃圾收集行程必須執行的工作量。 要處理的物件越少,GC 的執行速度就越快。

保留對象的參考時機

自動記憶體管理的一個副作用是,只要沒有參考這些物件,GC 就會清除未使用的物件。 例如,如果您建立局部變數來保存最上層檢視控制器或最上層視窗,然後讓這些變數消失在後方,有時可能會產生令人驚訝的副作用。

如果您未在靜態或實例變數中保留對象的參考,Mono 會愉快地在物件上呼叫 Dispose() 方法,而且它們會釋放對象的參考。 由於這可能是唯一未完成的參考,因此 Objective-C 運行時間會為您終結 物件。