系結類型參考指南
本文件說明可用來標註 API 合約檔案以驅動系結和產生的程式代碼的屬性清單
Xamarin.iOS 和 Xamarin.Mac API 合約是以 C# 撰寫的,大部分是以介面定義,以定義程式代碼呈現至 C# 的方式 Objective-C 。 此程式牽涉到介面宣告的混合,以及 API 合約可能需要的一些基本類型定義。 如需系結類型的簡介,請參閱我們的隨附指南 系結 Objective-C 連結庫。
類型定義
語法:
[BaseType (typeof (BTYPE))
interface MyType : [Protocol1, Protocol2] {
IntPtr Constructor (string foo);
}
合約定義中的每個介面,其屬性 [BaseType]
都會宣告所產生物件的基底類型。 在上述宣告中, MyType
會產生類別 C# 類型,以系結至 Objective-C 稱為 MyType
的類型。
如果您使用介面繼承語法在 typename 之後指定任何類型(在上述 Protocol1
範例中為 和 Protocol2
),這些介面的內容會內嵌,就好像這些介面是 合約 MyType
的一部分。
Xamarin.iOS 表面採用通訊協定的方式,就是將通訊協定中宣告的所有方法和屬性內嵌到類型本身。
下列示範如何在 Objective-C Xamarin.iOS 合約中定義 的宣告 UITextField
:
@interface UITextField : UIControl <UITextInput> {
}
會以 C# API 合約的形式撰寫如下:
[BaseType (typeof (UIControl))]
interface UITextField : UITextInput {
}
您可以將其他屬性套用至介面,以及設定 [BaseType]
屬性,來控制程式代碼產生的許多其他層面。
產生事件
Xamarin.iOS 和 Xamarin.Mac API 設計的其中一個功能是,我們將委派類別對應 Objective-C 為 C# 事件和回呼。 使用者可以依實例選擇是否要採用 Objective-C 程序設計模式,方法是指派給類別實例之類的屬性 Delegate
,以實作運行時間所呼叫的各種方法 Objective-C ,或選擇 C#樣式事件和屬性。
讓我們看看如何使用 Objective-C 模型的其中一個範例:
bool MakeDecision ()
{
return true;
}
void Setup ()
{
var scrollView = new UIScrollView (myRect);
scrollView.Delegate = new MyScrollViewDelegate ();
...
}
class MyScrollViewDelegate : UIScrollViewDelegate {
public override void Scrolled (UIScrollView scrollView)
{
Console.WriteLine ("Scrolled");
}
public override bool ShouldScrollToTop (UIScrollView scrollView)
{
return MakeDecision ();
}
}
在上述範例中,您可以看到我們選擇覆寫兩種方法,一個是卷動事件的通知,第二個是回呼,應該傳回布爾值,指示 scrollView
它是否應該捲動到頂端。
C# 模型可讓使用者使用 C# 事件語法或屬性語法來接聽通知,以連結預期傳回值的回呼。
這就是相同功能的 C# 程式代碼使用 Lambda 的方式:
void Setup ()
{
var scrollview = new UIScrollView (myRect);
// Event connection, use += and multiple events can be connected
scrollView.Scrolled += (sender, eventArgs) { Console.WriteLine ("Scrolled"); }
// Property connection, use = only a single callback can be used
scrollView.ShouldScrollToTop = (sv) => MakeDecision ();
}
因為事件不會傳回值(它們有 void 傳回類型),所以您可以連接多個複本。 ShouldScrollToTop
不是事件,而是具有此簽章之型UIScrollViewCondition
別的屬性:
public delegate bool UIScrollViewCondition (UIScrollView scrollView);
它會傳 bool
回值,在此情況下,Lambda 語法可讓我們只從 MakeDecision
函式傳回值。
系結產生器支持產生事件和屬性,以鏈接類別,例如UIScrollView
其 UIScrollViewDelegate
(妥善呼叫這些 Model 類別),其方式是使用 Events
和 Delegates
參數來標註您的[BaseType]
定義(如下所述)。
除了 [BaseType]
使用這些參數標註 之外,還必須通知產生器更多元件。
對於採用多個參數的事件(慣例 Objective-C 是委派類別中的第一個參數是傳送者對象的實例),您必須提供所產生 EventArgs
類別想要的名稱。 這會使用 [EventArgs]
Model 類別中方法宣告上的 屬性來完成。 例如:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
上述宣告會產生衍生 UIImagePickerImagePickedEventArgs
自 EventArgs
和 封裝參數 UIImage
和的 NSDictionary
類別。 產生器會產生下列專案:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
然後它會在 類別中 UIImagePickerController
公開下列內容:
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
傳回值的模型方法會以不同的方式系結。 這兩者都需要所產生 C# 委派的名稱(方法的簽章),以及在使用者未提供實作時傳回的預設值。
例如, ShouldScrollToTop
定義如下:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIScrollViewDelegate {
[Export ("scrollViewShouldScrollToTop:"), DelegateName ("UIScrollViewCondition"), DefaultValue ("true")]
bool ShouldScrollToTop (UIScrollView scrollView);
}
上述會建立 UIScrollViewCondition
具有上述簽章的委派,如果使用者未提供實作,則傳回值會是 true。
除了 [DefaultValue]
屬性之外,您也可以使用 [DefaultValueFromArgument]
指示產生器傳回呼叫中指定參數的值的屬性,或 [NoDefaultValue]
指示產生器沒有預設值的參數。
BaseTypeAttribute
語法:
public class BaseTypeAttribute : Attribute {
public BaseTypeAttribute (Type t);
// Properties
public Type BaseType { get; set; }
public string Name { get; set; }
public Type [] Events { get; set; }
public string [] Delegates { get; set; }
public string KeepRefUntil { get; set; }
}
BaseType.Name
您可以使用 Name
屬性來控制此類型將在世界上系結 Objective-C 的名稱。 這通常用來為 C# 類型提供符合 .NET Framework 設計指導方針的名稱,但對應至不符合該慣例的名稱 Objective-C 。
例如,在下列案例中,我們會將 Objective-CNSURLConnection
類型對應至 NSUrlConnection
,因為 .NET Framework 設計指導方針會使用 “Url” 而不是 “URL”:
[BaseType (typeof (NSObject), Name="NSURLConnection")]
interface NSUrlConnection {
}
指定的名稱會當做系結中產生的 [Register]
屬性值使用。 如果未 Name
指定,則會使用型別的簡短名稱做為所產生輸出中屬性的值 [Register]
。
BaseType.Events 和 BaseType.Delegates
這些屬性可用來驅動產生所產生類別中的 C#樣式事件。 它們可用來連結指定類別與其 Objective-C 委派類別。 在許多情況下,類別會使用委派類別來傳送通知和事件。 例如, BarcodeScanner
會有隨附 BardodeScannerDelegate
類別。 類別BarcodeScanner
通常會有一個Delegate
屬性,您會將 的實例BarcodeScannerDelegate
指派給 ,雖然可以運作,但您可能會想要向使用者公開類似 C# 的樣式事件介面,在這些情況下,您會使用 Events
屬性的 [BaseType]
和 Delegates
屬性。
這些屬性一律會設定在一起,而且必須具有相同數目的項目,並且保持同步。數位會 Delegates
針對您要包裝的每個弱型別委派包含一個字串,而且 Events
陣列會針對您想要與其產生關聯的每個類型包含一個類型。
[BaseType (typeof (NSObject),
Delegates=new string [] { "WeakDelegate" },
Events=new Type [] {typeof(UIAccelerometerDelegate)})]
public interface UIAccelerometer {
}
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIAccelerometerDelegate {
}
BaseType.KeepRefUntil
如果您在建立這個類別的新實例時套用這個屬性,該對象的實例將會保留在周圍,直到 叫用 所 KeepRefUntil
參考的方法為止。 當您不想讓使用者保留對象參考以使用程式代碼時,這可改善 API 的可用性。 這個屬性的值是 類別中Delegate
方法的名稱,因此您也必須搭配 和 Delegates
屬性使用這個 Events
。
下列範例示範如何在 Xamarin.iOS 中使用這項功能 UIActionSheet
:
[BaseType (typeof (NSObject), KeepRefUntil="Dismissed")]
[BaseType (typeof (UIView),
KeepRefUntil="Dismissed",
Delegates=new string [] { "WeakDelegate" },
Events=new Type [] {typeof(UIActionSheetDelegate)})]
public interface UIActionSheet {
}
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIActionSheetDelegate {
[Export ("actionSheet:didDismissWithButtonIndex:"), EventArgs ("UIButton")]
void Dismissed (UIActionSheet actionSheet, nint buttonIndex);
}
DesignatedDefaultCtorAttribute
當此屬性套用至介面定義時,它會在對應至init
選取器的預設 (generated) 建構函式上產生[DesignatedInitializer]
屬性。
DisableDefaultCtorAttribute
當此屬性套用至介面定義時,將防止產生器產生預設建構函式。
當您需要物件以 類別中的其他其中一個建構函式初始化時,請使用這個屬性。
PrivateDefaultCtorAttribute
當此屬性套用至介面定義時,它會將預設建構函式標示為私用。 這表示您仍然可以從延伸模組檔案內部具現化此類別的物件,但您類別的使用者將無法存取它。
CategoryAttribute
在類型定義上使用這個屬性來系結 Objective-C 類別,並公開這些類別做為 C# 擴充方法,以鏡像 Objective-C 方式公開功能。
類別是一種 Objective-C 機制,可用來擴充類別中可用的方法和屬性集。 在實務上,當特定架構在 中連結時,它們會用來擴充基類的功能(例如NSObject
UIKit
),使其方法可供使用,但只有在新架構已連結時。 在其他某些情況下,它們用來依功能來組織類別中的功能。 它們與 C# 擴充方法類似。
這就是類別在 中的 Objective-C外觀:
@interface UIView (MyUIViewExtension)
-(void) makeBackgroundRed;
@end
上述範例位於連結庫上,這個連結庫會使用 方法 makeBackgroundRed
擴充 的UIView
實例。
若要系結這些屬性,您可以在 [Category]
介面定義上使用 屬性。 使用 [Category]
屬性時,屬性的意義會從用來指定要擴充的 [BaseType]
基類變更為要擴充的類型。
以下顯示延伸模組如何 UIView
系結並轉換成 C# 擴充方法:
[BaseType (typeof (UIView))]
[Category]
interface MyUIViewExtension {
[Export ("makeBackgroundRed")]
void MakeBackgroundRed ();
}
上述會建立包含 MyUIViewExtension
擴充方法的 MakeBackgroundRed
類別。 這表示您現在可以在任何子類別上UIView
呼叫 MakeBackgroundRed
,並提供您取得Objective-C的相同功能。
在某些情況下,您會在類別內找到 靜態 成員,如下列範例所示:
@interface FooObject (MyFooObjectExtension)
+ (BOOL)boolMethod:(NSRange *)range;
@end
這會導致 類別 C# 介面定義不正確 :
[Category]
[BaseType (typeof (FooObject))]
interface FooObject_Extensions {
// Incorrect Interface definition
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
不正確,因為若要使用 BoolMethod
擴充功能,您需要 實例 FooObject
,但系結 ObjC 靜態 延伸模組,這是因為 C# 擴充方法實作方式的緣故,這是副作用。
使用上述定義的唯一方法是下列醜陋的程式代碼:
(null as FooObject).BoolMethod (range);
若要避免這種情況,建議將定義內嵌 BoolMethod
在介面定義本身內 FooObject
,這可讓您呼叫此延伸模組,就像其用途 FooObject.BoolMethod (range)
一樣。
[BaseType (typeof (NSObject))]
interface FooObject {
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
每當我們在定義內[Category]
找到[Static]
成員時,我們都會發出警告 (BI1117)。 如果您真的想要在定義內[Category]
有[Static]
成員,則可以使用 [Category (allowStaticMembers: true)]
或 ,或使用 裝飾您的成員或[Category]
介面定義[Internal]
來讓警告無聲。
StaticAttribute
當這個屬性套用至類別時,它只會產生靜態類別,而這個類別不會衍生自 NSObject
,因此 [BaseType]
會忽略屬性。 靜態類別可用來裝載您想要公開的 C 公用變數。
例如:
[Static]
interface CBAdvertisement {
[Field ("CBAdvertisementDataServiceUUIDsKey")]
NSString DataServiceUUIDsKey { get; }
將會使用下列 API 產生 C# 類別:
public partial class CBAdvertisement {
public static NSString DataServiceUUIDsKey { get; }
}
通訊協定/模型定義
模型通常由通訊協議實作使用。 不同的是,運行時間只會向 Objective-C 實際覆寫的方法註冊。 否則,將不會註冊 方法。
這通常表示當您將已標幟為 ModelAttribute
的類別子類別時,您不應該呼叫基底方法。 呼叫該方法將會擲回下列例外狀況:Foundation.You_Should_Not_Call_base_In_This_Method。 您應該針對您覆寫的任何方法,在您的子類別上實作整個行為。
AbstractAttribute
根據預設,屬於通訊協議的成員並非必要。 這可讓使用者只從 C# 中的 類別衍生,並只覆寫他們關心的方法,來建立 物件的子類別 Model
。 有時候合約 Objective-C 會要求使用者提供此方法的實作(這些實作會加上 @required
中的 Objective-C指示詞)。 在這些情況下,您應該使用 屬性來 [Abstract]
標記這些方法。
屬性 [Abstract]
可以套用至方法或屬性,並讓產生器將產生的成員標示為抽象,而 類別則為抽象類。
下列內容取自 Xamarin.iOS:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UITableViewDataSource {
[Export ("tableView:numberOfRowsInSection:")]
[Abstract]
nint RowsInSection (UITableView tableView, nint section);
}
DefaultValueAttribute
如果使用者未在 Model 物件中提供這個特定方法的方法,則指定模型方法所傳回的預設值
語法:
public class DefaultValueAttribute : Attribute {
public DefaultValueAttribute (object o);
public object Default { get; set; }
}
例如,在下列類別的虛數委派類別 Camera
中,我們會提供 ShouldUploadToServer
,其會公開為 類別上的 Camera
屬性。 如果 類別的使用者 Camera
未將值明確設定為可以回應 true 或 false 的 Lambda,則在此情況下傳回的預設值會是 false,我們在 屬性中指定的 DefaultValue
值:
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("camera:shouldPromptForAction:"), DefaultValue (false)]
bool ShouldUploadToServer (Camera camera, CameraAction action);
}
如果使用者在虛數類別中設定處理程式,則會忽略此值:
var camera = new Camera ();
camera.ShouldUploadToServer = (camera, action) => return SomeDecision ();
另請參閱: [NoDefaultValue]
、 [DefaultValueFromArgument]
。
DefaultValueFromArgumentAttribute
語法:
public class DefaultValueFromArgumentAttribute : Attribute {
public DefaultValueFromArgumentAttribute (string argument);
public string Argument { get; }
}
在傳回模型類別值的方法上提供時,這個屬性會指示產生器在使用者未提供自己的方法或 Lambda 時傳回指定參數的值。
範例:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, nfloat progress);
}
在上述案例中,如果類別的用戶 NSAnimation
選擇使用任何 C# 事件/屬性,且未設定 NSAnimation.ComputeAnimationCurve
為方法或 Lambda,則傳回值會是傳入 progress 參數中的值。
請參閱: [NoDefaultValue]
、 [DefaultValue]
IgnoredInDelegateAttribute
有時候,將來自 Model 類別的事件或委派屬性公開至主機類別是合理的,因此新增這個屬性會指示產生器避免產生任何裝飾的方法。
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
[Export ("imagePickerController:didFinishPickingImage:"), IgnoredInDelegate)] // No event generated for this method
void FinishedPickingImage (UIImagePickerController picker, UIImage image);
}
DelegateNameAttribute
此屬性用於傳回值以設定要使用的委派簽章名稱的 Model 方法中。
範例:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, float progress);
}
使用上述定義,產生器會產生下列公用宣告:
public delegate float NSAnimationProgress (MonoMac.AppKit.NSAnimation animation, float progress);
DelegateApiNameAttribute
這個屬性可用來讓產生器變更主機類別中產生的屬性名稱。 有時候,當 FooDelegate 類別方法的名稱對 Delegate 類別有意義時,它很有用,但在主機類別中看起來會很奇怪做為屬性。
此外,當您有兩個以上的多載方法,讓它們命名為 FooDelegate 類別,但您想要以更好的指定名稱在主機類別中公開它們時,這非常有用(且需要)。
範例:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateApiName ("ComputeAnimationCurve"), DelegateName ("Func<NSAnimation, float, float>"), DefaultValueFromArgument ("progress")]
float GetValueForProgress (NSAnimation animation, float progress);
}
使用上述定義,產生器會在主機類別中產生下列公用宣告:
public Func<NSAnimation, float, float> ComputeAnimationCurve { get; set; }
EventArgsAttribute
對於採用多個參數的事件(慣例 Objective-C 是委派類別中的第一個參數是 sender 對象的實例),您必須提供您想要產生 EventArgs 類別的名稱。 這會使用 [EventArgs]
類別 Model
中方法宣告上的 屬性來完成。
例如:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
上述宣告會產生衍生 UIImagePickerImagePickedEventArgs
自 EventArgs 的類別,並封裝 參數 UIImage
和 NSDictionary
。 產生器會產生下列專案:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
然後它會在 類別中 UIImagePickerController
公開下列內容:
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
EventNameAttribute
這個屬性可用來讓產生器變更類別中產生之事件或屬性的名稱。 有時候,當 Model 類別方法的名稱對模型類別有意義,但在原始類別中看起來會很奇怪,做為事件或屬性時,它很有用。
例如,使用 UIWebView
中的下列位 UIWebViewDelegate
:
[Export ("webViewDidFinishLoad:"), EventArgs ("UIWebView"), EventName ("LoadFinished")]
void LoadingFinished (UIWebView webView);
上述會 LoadingFinished
公開 為中的 UIWebViewDelegate
方法,但 LoadFinished
作為 要連結至 中的 UIWebView
事件:
var webView = new UIWebView (...);
webView.LoadFinished += delegate { Console.WriteLine ("done!"); }
ModelAttribute
當您將 [Model]
屬性套用至合約 API 中的類型定義時,如果使用者已覆寫 類別中的方法,運行時間將會產生特殊的程式代碼,而這個程式代碼只會在類別中呈現方法的調用。 此屬性通常會套用至包裝 Objective-C 委派類別的所有 API。
運行時間也會產生 Objective-C 符合對應通訊協定名稱的類別。
您可以透過兩種方式自訂 類別的名稱 Objective-C :
設定
AutoGeneratedName = true
:[Model (AutoGeneratedName = true)]
這會使運行時間產生 Objective-C 類型的唯一名稱。 名稱目前是以元件名稱和模型類型的完整名稱為基礎(未來可能會變更)。
明確指定名稱:
[Model (Name = "CustomName")]
建議使用 AutoGeneratedName = true
。 在 .NET 中,一律會產生名稱(除非它已明確指定為 2.above),而且 AutoGeneratedName
屬性已不存在。
NoDefaultValueAttribute
指定模型上的 方法未提供預設傳回值。
這可藉由回應false
Objective-C運行時間要求來判斷指定的選取器是否在此類別中實作,來與Objective-C運行時間搭配運作。
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("shouldDisplayPopup"), NoDefaultValue]
bool ShouldUploadToServer ();
}
請參閱: [DefaultValue]
、 [DefaultValueFromArgument]
通訊協定
通訊 Objective-C 協定概念並不存在於 C# 中。 通訊協定與 C# 介面類似,但不同之處在於,並非所有在通訊協定中宣告的方法和屬性都必須由採用它的類別實作。 而不是某些方法和屬性是選擇性的。
某些通訊協定通常用來做為模型類別,這些通訊協議應該使用 [Model]
屬性來系結。
[BaseType (typeof (NSObject))]
[Model, Protocol]
interface MyProtocol {
// Use [Abstract] when the method is defined in the @required section
// of the protocol definition in Objective-C
[Abstract]
[Export ("say:")]
void Say (string msg);
[Export ("listen")]
void Listen ();
}
從 Xamarin.iOS 7.0 開始,已納入新的改良通訊協議系結功能。 包含 [Protocol]
屬性的任何定義實際上都會產生三個支持類別,以大幅改善您取用通訊協定的方式:
// Full method implementation, contains all methods
class MyProtocol : IMyProtocol {
public void Say (string msg);
public void Listen (string msg);
}
// Interface that contains only the required methods
interface IMyProtocol: INativeObject, IDisposable {
[Export ("say:")]
void Say (string msg);
}
// Extension methods
static class IMyProtocol_Extensions {
public static void Optional (this IMyProtocol this, string msg);
}
}
類別 實 作提供完整的抽象類,您可以覆寫 的個別方法並取得完整的型別安全性。 但由於 C# 不支援多個繼承,因此在某些情況下,您可能需要不同的基類,但仍想要實作介面。
這是產生的 介面定義 所在的位置。 它是具有通訊協定中所有必要方法的介面。 這可讓想要實作通訊協議的開發人員只實作 介面。 運行時間會自動將類型註冊為採用通訊協定。
請注意,介面只會列出必要的方法,而且會公開選擇性方法。 這表示採用通訊協議的類別會取得必要方法的完整型別檢查,但必須針對選擇性通訊協定方法使用導出屬性和比對簽章手動使用弱型別。
為了方便取用使用通訊協定的 API,系結工具也會產生擴充方法類別,以公開所有選擇性方法。 這表示只要您使用 API,您就能夠將通訊協定視為具有所有方法。
如果您想要在 API 中使用通訊協定定義,您必須在 API 定義中撰寫基本架構空白介面。 如果您想要在 API 中使用 MyProtocol,您需要執行此動作:
[BaseType (typeof (NSObject))]
[Model, Protocol]
interface MyProtocol {
// Use [Abstract] when the method is defined in the @required section
// of the protocol definition in Objective-C
[Abstract]
[Export ("say:")]
void Say (string msg);
[Export ("listen")]
void Listen ();
}
interface IMyProtocol {}
[BaseType (typeof(NSObject))]
interface MyTool {
[Export ("getProtocol")]
IMyProtocol GetProtocol ();
}
之所以需要上述,是因為在系結時 IMyProtocol
不存在,這就是為什麼您需要提供空的介面。
採用通訊協議產生的介面
每當您實作針對通訊協議產生的其中一個介面時,如下所示:
class MyDelegate : NSObject, IUITableViewDelegate {
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
必要介面方法的實作會以適當的名稱導出,因此它相當於下列專案:
class MyDelegate : NSObject, IUITableViewDelegate {
[Export ("getRowHeight:")]
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
這適用於所有必要的通訊協議成員,但有選擇性選取器需要注意的特殊案例。
使用基類時,選擇性通訊協議成員會以相同方式處理:
public class UrlSessionDelegate : NSUrlSessionDownloadDelegate {
public override void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
但在使用通訊協定介面時,需要新增 [Export]。 當您從覆寫開始新增它時,IDE 會透過自動完成來新增它。
public class UrlSessionDelegate : NSObject, INSUrlSessionDownloadDelegate {
[Export ("URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:")]
public void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
兩者在運行時間有輕微的行為差異:
- 基類的使用者(例如 NSUrlSessionDownloadDelegate)提供所有必要的和選擇性選取器,並傳回合理的預設值。
- 介面的使用者(例如 INSUrlSessionDownloadDelegate)只會回應所提供的確切選取器。
某些罕見的類別在這裏的行為可能會有所不同。 不過,在幾乎所有情況下,使用都安全。
通訊協定內嵌
當您系結已宣告為採用通訊協定的現有 Objective-C 類型時,您會想要直接內嵌通訊協定。 若要這樣做,只要將您的通訊協定宣告為沒有任何 [BaseType]
屬性的介面,並在介面的基底介面清單中列出通訊協定。
範例:
interface SpeakProtocol {
[Export ("say:")]
void Say (string msg);
}
[BaseType (typeof (NSObject))]
interface Robot : SpeakProtocol {
[Export ("awake")]
bool Awake { get; set; }
}
成員定義
本節中的屬性會套用至類型的個別成員:屬性和方法宣告。
AlignAttribute
用來指定屬性傳回類型的對齊值。 某些屬性會取得必須對齊特定界限之位址的指標(在 Xamarin.iOS 中,這種情況會發生,例如 GLKBaseEffect
某些必須對齊 16 位元組的屬性)。 您可以使用這個屬性來裝飾 getter,並使用對齊值。 與 API 整合時,這通常與 OpenTK.Vector4
和 OpenTK.Matrix4
型別搭配 Objective-C 使用。
範例:
public interface GLKBaseEffect {
[Export ("constantColor")]
Vector4 ConstantColor { [Align (16)] get; set; }
}
AppearanceAttribute
屬性 [Appearance]
僅限於 iOS 5,其中引進了外觀管理員。
屬性 [Appearance]
可以套用至任何參與 UIAppearance
架構的方法或屬性。 當這個屬性套用至類別中的方法或屬性時,它會指示系結產生器建立強型別外觀類別,用來設定此類別的所有實例樣式,或符合特定準則的實例。
範例:
public interface UIToolbar {
[Export ("setBackgroundImage:forToolbarPosition:barMetrics:")]
[Appearance]
void SetBackgroundImage (UIImage backgroundImage, UIToolbarPosition position, UIBarMetrics barMetrics);
[Export ("backgroundImageForToolbarPosition:barMetrics:")]
[Appearance]
UIImage GetBackgroundImage (UIToolbarPosition position, UIBarMetrics barMetrics);
}
上述會在 UIToolbar 中產生下列程式代碼:
public partial class UIToolbar {
public partial class UIToolbarAppearance : UIView.UIViewAppearance {
public virtual void SetBackgroundImage (UIImage backgroundImage, UIToolbarPosition position, UIBarMetrics barMetrics);
public virtual UIImage GetBackgroundImage (UIToolbarPosition position, UIBarMetrics barMetrics)
}
public static new UIToolbarAppearance Appearance { get; }
public static new UIToolbarAppearance AppearanceWhenContainedIn (params Type [] containers);
}
AutoReleaseAttribute (Xamarin.iOS 5.4)
[AutoReleaseAttribute]
使用 on 方法和屬性,將方法調用包裝至 中的NSAutoReleasePool
方法。
有 Objective-C 一些方法會傳回新增至預設 NSAutoReleasePool
的值。 根據預設,這些會移至您的線程 NSAutoReleasePool
,但因為 Xamarin.iOS 也會保留對象的參考,只要 Managed 物件存留,您可能不想保留額外的參考 NSAutoReleasePool
,這隻會清空,直到您的線程將控制權傳回下一個線程為止,或回到 main 迴圈。
這個屬性會套用至重屬性(例如 UIImage.FromFile
),以傳回已新增至預設 NSAutoReleasePool
的物件。 如果沒有這個屬性,只要線程未將控制權傳回主要迴圈,影像就會保留。 Uf 您的線程是某種背景下載程式,一律保持運作,並等待工作,永遠不會釋放影像。
ForcedTypeAttribute
[ForcedTypeAttribute]
用來強制建立Managed型別,即使傳回的Unmanaged物件不符合系結定義中所述的類型也一樣。
當標頭中所述的類型與原生方法的傳回型別不符時,這非常有用,例如從 NSURLSession
取得下列Objective-C定義:
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
它清楚地指出它會傳回 實例,但它卻傳NSURLSessionTask
回 NSURLSessionDownloadTask
,這是超類別,因此無法轉換成 NSURLSessionDownloadTask
。 由於我們處於類型安全的內容中, InvalidCastException
因此會發生 。
若要符合標頭描述並避免 InvalidCastException
, [ForcedTypeAttribute]
則會使用 。
[BaseType (typeof (NSObject), Name="NSURLSession")]
interface NSUrlSession {
[Export ("downloadTaskWithRequest:")]
[return: ForcedType]
NSUrlSessionDownloadTask CreateDownloadTask (NSUrlRequest request);
}
[ForcedTypeAttribute]
也接受預設為 的Owns
false
[ForcedType (owns: true)]
布爾值。 owns 參數可用來遵循 Core Foundation 對象的擁有權原則。
[ForcedTypeAttribute]
只有在參數、屬性和傳回值上才有效。
BindAsAttribute
[BindAsAttribute]
允許將和 NSValue
NSString
(列舉) 系結NSNumber
到更精確的 C# 類型。 屬性可用來透過原生 API 建立更好、更精確的 .NET API。
您可以使用 裝飾方法(在傳回值上)、參數和屬性 BindAs
。 唯一[Protocol]
的限制是您的成員不得位於 或 [Model]
介面內。
例如:
[return: BindAs (typeof (bool?))]
[Export ("shouldDrawAt:")]
NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect);
會輸出:
[Export ("shouldDrawAt:")]
bool? ShouldDraw (CGRect rect) { ... }
在內部,我們將執行 bool?
<-NSNumber
> 和<CGRect
->NSValue
轉換。
目前支援的封裝類型如下:
NSValue
NSNumber
NSString
NSValue
支援下列 C# 資料類型從/封裝成 NSValue
:
- CGAffineTransform
- NSRange
- CGVector
- SCNMatrix4
- CLLocationCoordinate2D
- SCNVector3
- SCNVector4
- CGPoint / PointF
- CGRect / RectangleF
- CGSize / SizeF
- UIEdgeInsets
- UIOffset
- MKCoordinateSpan
- CMTimeRange
- CMTime
- CMTimeMapping
- CATransform3D
NSNumber
支援下列 C# 資料類型從/封裝成 NSNumber
:
- bool
- byte
- double
- float
- short
- int
- long
- sbyte
- ushort
- uint
- ulong
- nfloat
- nint
- nuint
- 列舉
NSString
[BindAs]
適用於 NSString 常數 支援的列舉,因此您可以建立更好的 .NET API,例如:
[BindAs (typeof (CAScroll))]
[Export ("supportedScrollMode")]
NSString SupportedScrollMode { get; set; }
會輸出:
[Export ("supportedScrollMode")]
CAScroll SupportedScrollMode { get; set; }
只有當提供的列舉型[BindAs]
別是由 NSString 常數所支援時,我們才會處理 enum
<->NSString
轉換。
陣列
[BindAs]
也支援任何支援的型別陣列,您可以有下列 API 定義做為範例:
[return: BindAs (typeof (CAScroll []))]
[Export ("getScrollModesAt:")]
NSString [] GetScrollModes ([BindAs (typeof (CGRect []))] NSValue [] rects);
會輸出:
[Export ("getScrollModesAt:")]
CAScroll? [] GetScrollModes (CGRect [] rects) { ... }
參數rects
會封裝成 NSArray
,NSValue
其中包含每個 CGRect
的 ,並在傳回中,您會收到使用所NSStrings
傳回NSArray
包含 之 值所建立的陣列CAScroll?
。
BindAttribute
屬性 [Bind]
有兩個在套用至方法或屬性宣告時使用一個,另一個則套用至屬性中的個別 getter 或 setter。
當用於方法或屬性時,屬性的效果 [Bind]
是產生叫用指定選取器的方法。 但是產生的方法不會以 [Export]
屬性裝飾,這表示它無法參與覆寫方法。 這通常會與 屬性搭配 [Target]
使用,以實作 Objective-C 擴充方法。
例如:
public interface UIView {
[Bind ("drawAtPoint:withFont:")]
SizeF DrawString ([Target] string str, CGPoint point, UIFont font);
}
在 getter 或 setter 中使用時, [Bind]
屬性會用於在產生屬性的 getter 和 setter Objective-C 選取器名稱時,用來改變程式代碼產生器推斷的預設值。 根據預設,當您將名稱 fooBar
為的屬性加上旗標時,產生器會產生 fooBar
getter 和 setFooBar:
setter 的匯出。 在少數情況下, Objective-C 不會遵循此慣例,通常會將 getter 名稱變更為 isFooBar
。
您會使用這個屬性來通知產生器。
例如:
// Default behavior
[Export ("active")]
bool Active { get; set; }
// Custom naming with the Bind attribute
[Export ("visible")]
bool Visible { [Bind ("isVisible")] get; set; }
AsyncAttribute
僅適用於 Xamarin.iOS 6.3 和更新版。
這個屬性可以套用至採用完成處理程式作為最後一個自變數的方法。
您可以在 [Async]
最後一個自變數為回呼的方法上使用 屬性。 當您將這個套用至方法時,系結產生器會產生具有 後綴 Async
的該方法版本。 如果回呼不接受任何參數,則傳回值會是 Task
,如果回呼採用參數,則結果會是 Task<T>
。
[Export ("upload:complete:")]
[Async]
void LoadFile (string file, NSAction complete)
下列會產生這個異步方法:
Task LoadFileAsync (string file);
如果回呼採用多個參數,您應該設定 ResultType
或 ResultTypeName
來指定所產生型別的所需名稱,以保存所有屬性。
delegate void OnComplete (string [] files, nint byteCount);
[Export ("upload:complete:")]
[Async (ResultTypeName="FileLoading")]
void LoadFiles (string file, OnComplete complete)
下列將產生這個異步方法,其中 FileLoading
包含可存取 files
和 byteCount
的屬性:
Task<FileLoading> LoadFile (string file);
如果回呼的最後一個 NSError
參數是 ,則產生的 Async
方法會檢查值是否不是 Null,如果是這種情況,產生的異步方法將會設定工作例外狀況。
[Export ("upload:onComplete:")]
[Async]
void Upload (string file, Action<string,NSError> onComplete);
上述會產生下列異步方法:
Task<string> UploadAsync (string file);
而發生錯誤時,產生的工作會將例外狀況設定為 NSErrorException
,以包裝產生的 NSError
。
AsyncAttribute.ResultType
使用這個屬性來指定傳 Task
回物件的值。 此參數採用現有的類型,因此必須在其中一個核心 API 定義中定義。
AsyncAttribute.ResultTypeName
使用這個屬性來指定傳 Task
回物件的值。 此參數會採用所需類型名稱的名稱,產生器會產生一系列屬性,其中一個用於回呼採用的每個參數。
AsyncAttribute.MethodName
使用這個屬性來自定義產生的異步方法名稱。 預設值是使用 方法的名稱,並附加 「Async」 文字,您可以使用這個來變更此預設值。
DesignatedInitializerAttribute
當這個屬性套用至建構函式時,它會在最終平臺元件中產生相同的 [DesignatedInitializer]
屬性。 這是為了協助 IDE 指出子類別中應該使用哪一個建構函式。
這應該對應至 Objective-C的 __attribute__((objc_designated_initializer))
/clang 用法。
DisableZeroCopyAttribute
這個屬性會套用至字串參數或字串屬性,並指示程式代碼產生器不要使用此參數的零複製字串封送處理,而是從 C# 字串建立新的 NSString 實例。
只有在您指示產生器使用 --zero-copy
命令行選項或設定元件層級屬性 ZeroCopyStringsAttribute
時,才需要在字串上使用零複製字串封送處理。
如果 屬性宣告 Objective-C 為 retain
或 assign
屬性,而不是 copy
屬性,則這是必要的。 這些通常會發生在開發人員錯誤地「優化」的第三方連結庫中。 一般而言,或 屬性不正確,retain
因為 NSMutableString
或 assign
NSString
使用者衍生的類別NSString
可能會改變字串的內容,而不需要連結庫程式代碼的知識,巧妙地中斷應用程式。 這通常是因為過早優化而發生。
下列顯示 中的 Objective-C兩個這類屬性:
@property(nonatomic,retain) NSString *name;
@property(nonatomic,assign) NSString *name2;
DisposeAttribute
當您將 套用 [DisposeAttribute]
至 類別時,您會提供將新增至 Dispose()
類別之方法實作的代碼段。
Dispose
由於工具會自動產生 bgen
方法,因此您必須使用 [Dispose]
屬性,在產生的Dispose
方法實作中插入一些程序代碼。
例如:
[BaseType (typeof (NSObject))]
[Dispose ("if (OpenConnections > 0) CloseAllConnections ();")]
interface DatabaseConnection {
}
ExportAttribute
屬性 [Export]
是用來標記要公開給 Objective-C 運行時間的方法或屬性。 這個屬性會在系結工具與實際的 Xamarin.iOS 和 Xamarin.Mac 運行時間之間共用。 對於方法,參數會逐字傳遞至產生的程序代碼,如果是屬性,則會根據基底宣告產生 getter 和 setter 匯出(如需如何改變系結工具行為相關信息的 一節 [BindAttribute]
)。
語法:
public enum ArgumentSemantic {
None, Assign, Copy, Retain.
}
[AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)]
public class ExportAttribute : Attribute {
public ExportAttribute();
public ExportAttribute (string selector);
public ExportAttribute (string selector, ArgumentSemantic semantic);
public string Selector { get; set; }
public ArgumentSemantic ArgumentSemantic { get; set; }
}
選取 器 代表所系結之基礎 Objective-C 方法或屬性的名稱。
ExportAttribute.ArgumentSemantic
FieldAttribute
這個屬性用來公開 C 全域變數做為視需要載入並公開至 C# 程式代碼的欄位。 這通常是取得 C 或 Objective-C 中定義的常數值,而且可能是某些 API 中使用的令牌,或是其值不透明且必須由使用者程式代碼依序使用。
語法:
public class FieldAttribute : Attribute {
public FieldAttribute (string symbolName);
public FieldAttribute (string symbolName, string libraryName);
public string SymbolName { get; set; }
public string LibraryName { get; set; }
}
symbolName
是要連結的 C 符號。 根據預設,這會從定義類型之命名空間推斷其名稱的連結庫載入。 如果這不是查閱符號的連結庫,您應該傳遞 libraryName
參數。 如果您要連結靜態庫,請使用 __Internal
作為 libraryName
參數。
產生的屬性一律是靜態的。
以 Field 屬性標示的屬性可以是下列類型:
NSString
NSArray
nint
/int
/long
nuint
/uint
/ulong
nfloat
/float
double
CGSize
System.IntPtr
- 列舉
NSString 常數所支援的列舉不支援 Setter,但視需要手動系結這些列舉。
範例:
[Static]
interface CameraEffects {
[Field ("kCameraEffectsZoomFactorKey", "CameraLibrary")]
NSString ZoomFactorKey { get; }
}
InternalAttribute
屬性 [Internal]
可以套用至方法或屬性,而且其效果是使用 C# 關鍵詞標記產生的程式代碼 internal
,讓程式代碼只能供產生的元件中的程式代碼存取。 這通常用來隱藏太低層級的 API,或提供您想要改善的次佳公用 API,或針對產生器不支援的 API,而且需要一些手動編碼。
當您設計系結時,通常會使用這個屬性隱藏方法或屬性,併為方法或屬性提供不同的名稱,然後在 C# 互補支援檔案上,新增一個公開基礎功能的強型別包裝函式。
例如:
[Internal]
[Export ("setValue:forKey:")]
void _SetValueForKey (NSObject value, NSObject key);
[Internal]
[Export ("getValueForKey:")]
NSObject _GetValueForKey (NSObject key);
然後,在您的支援檔案中,您可能會有一些程序代碼,如下所示:
public NSObject this [NSObject idx] {
get {
return _GetValueForKey (idx);
}
set {
_SetValueForKey (value, idx);
}
}
IsThreadStaticAttribute
此屬性會標幟要以 .NET [ThreadStatic]
屬性標註之屬性的備份欄位。 如果欄位是線程靜態變數,這會很有用。
MarshalNativeExceptions (Xamarin.iOS 6.0.6)
這個屬性會讓方法支援原生 (Objective-C) 例外狀況。
叫用不會直接呼叫 objc_msgSend
,而是會經歷自定義蹦床,以攔截 ObjectiveC 例外狀況,並將其封送處理成受控例外狀況。
目前只支持少數 objc_msgSend
簽章(當使用系結的應用程式原生連結失敗時,您將會發現簽章是否不受支援,但有遺漏的xamarin__objc_msgSend 符號),但可以在要求中新增更多簽章。
NewAttribute
這個屬性會套用至方法和屬性,讓產生器在宣告前面產生 new
關鍵詞。
當基類中已存在的子類別中引進相同的方法或屬性名稱時,它會用來避免編譯程式警告。
NotificationAttribute
您可以將此屬性套用至字段,讓產生器產生強型別協助程式 Notifications 類別。
這個屬性可以在沒有自變數的情況下用於沒有承載的通知,或者您可以指定 System.Type
參考 API 定義中另一個介面的,通常是以 “EventArgs” 結尾的名稱。 產生器會將 介面轉換成子類別的 EventArgs
類別,並將包含該處所列的所有屬性。 屬性 [Export]
應該用於 類別, EventArgs
以列出用來查閱 Objective-C 字典以擷取值之索引鍵的名稱。
例如:
interface MyClass {
[Notification]
[Field ("MyClassDidStartNotification")]
NSString DidStartNotification { get; }
}
上述程式代碼會產生具有下列方法的巢狀類別 MyClass.Notifications
:
public class MyClass {
[..]
public Notifications {
public static NSObject ObserveDidStart (EventHandler<NSNotificationEventArgs> handler)
public static NSObject ObserveDidStart (NSObject objectToObserve, EventHandler<NSNotificationEventArgs> handler)
}
}
然後,程式代碼的使用者可以使用類似下列程式代碼,輕鬆地訂閱張貼至 NSDefaultCenter 的通知:
var token = MyClass.Notifications.ObserverDidStart ((notification) => {
Console.WriteLine ("Observed the 'DidStart' event!");
});
或設定要觀察的特定物件。 如果您傳遞 null
至 objectToObserve
此方法的行為就像它的其他對等一樣。
var token = MyClass.Notifications.ObserverDidStart (objectToObserve, (notification) => {
Console.WriteLine ("Observed the 'DidStart' event on objectToObserve!");
});
傳 ObserveDidStart
回的值可用來輕鬆地停止接收通知,如下所示:
token.Dispose ();
或者,您可以呼叫 NSNotification.DefaultCenter.RemoveObserver 並傳遞令牌。 如果您的通知包含參數,您應該指定協助程式 EventArgs
介面,如下所示:
interface MyClass {
[Notification (typeof (MyScreenChangedEventArgs)]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
// The helper EventArgs declaration
interface MyScreenChangedEventArgs {
[Export ("ScreenXKey")]
nint ScreenX { get; set; }
[Export ("ScreenYKey")]
nint ScreenY { get; set; }
[Export ("DidGoOffKey")]
[ProbePresence]
bool DidGoOff { get; }
}
上述會使用 MyScreenChangedEventArgs
索引鍵產生 具有 ScreenX
和 ScreenY
屬性的類別,以分別使用索引鍵ScreenXKey
和ScreenYKey
套用適當的轉換,從 NSNotification.UserInfo 字典擷取數據。 屬性 [ProbePresence]
會用於產生器,以在 中 UserInfo
設定索引鍵,而不是嘗試擷取值。 這用於索引鍵存在是值的情況(通常是布爾值)。
這可讓您撰寫如下的程式代碼:
var token = MyClass.NotificationsObserveScreenChanged ((notification) => {
Console.WriteLine ("The new screen dimensions are {0},{1}", notification.ScreenX, notification.ScreenY);
});
在某些情況下,字典上傳遞的值沒有相關聯的常數。 Apple 有時會使用公用符號常數,有時使用字串常數。 根據預設, [Export]
所提供 EventArgs
類別中的屬性會使用指定的名稱作為要在運行時間查閱的公用符號。 如果不是這種情況,而是應該查詢為字串常數,然後將值傳遞 ArgumentSemantic.Assign
至 Export 屬性。
Xamarin.iOS 8.4 的新功能
有時候,通知會開始生活,而沒有任何自變數,因此可以接受不使用自變數的使用 [Notification]
。 但有時候會引進通知的參數。 若要支援此案例,可以套用屬性一次以上。
如果您正在開發系結,而且想要避免中斷現有的使用者程式代碼,您會從下列項目開啟現有的通知:
interface MyClass {
[Notification]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
在列出通知屬性兩次的版本中,如下所示:
interface MyClass {
[Notification]
[Notification (typeof (MyScreenChangedEventArgs)]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
NullAllowedAttribute
當此屬性套用至屬性時,它會將 屬性標示為允許指派值 null
給它。 這隻適用於參考型別。
當這個套用至方法簽章中的參數時,表示指定的參數可以是 Null,而且不應該執行任何檢查來傳遞 null
值。
如果參考型別沒有這個屬性,系結工具會在傳遞Objective-C值之前產生所指派值的檢查,併產生檢查,如果指派的值為 null
,則會擲回 ArgumentNullException
。
例如:
// In properties
[NullAllowed]
UIImage IconFile { get; set; }
// In methods
void SetImage ([NullAllowed] UIImage image, State forState);
OverrideAttribute
使用這個屬性來指示系結產生器,這個特定方法的系結應該以 override
關鍵詞標示。
PreSnippetAttribute
您可以使用這個屬性插入一些程式碼,以在驗證輸入參數之後插入,但在程式代碼呼叫 Objective-C之前。
範例:
[Export ("demo")]
[PreSnippet ("var old = ViewController;")]
void Demo ();
PrologueSnippetAttribute
您可以使用這個屬性來插入一些程式碼,以在產生的方法中驗證任何參數之前插入。
範例:
[Export ("demo")]
[Prologue ("Trace.Entry ();")]
void Demo ();
PostGetAttribute
指示系結產生器從這個類別叫用指定的屬性,從中擷取值。
這個屬性通常用來重新整理快取,以指向參考物件,讓物件圖形保持參考。 它通常會顯示在程序代碼中,其具有 [新增/移除] 等作業。 使用此方法,以便在新增或移除元素之後,更新內部快取,以確保我們會保留實際使用中物件的Managed參考。 這是可能的,因為系結工具會產生指定系結中所有參考物件的支援欄位。
範例:
[BaseType (typeof (NSObject))]
public interface NSOperation {
[Export ("addDependency:")][PostGet ("Dependencies")]
void AddDependency (NSOperation op);
[Export ("removeDependency:")][PostGet ("Dependencies")]
void RemoveDependency (NSOperation op);
[Export ("dependencies")]
NSOperation [] Dependencies { get; }
}
在此情況下, Dependencies
在加入或移除 物件的相依性 NSOperation
之後,將會叫用 屬性,確保我們有代表實際載入物件的圖形,以防止記憶體流失和記憶體損毀。
PostSnippetAttribute
您可以使用這個屬性插入一些 C# 原始程式碼,以在程式碼叫用基礎 Objective-C 方法之後插入
範例:
[Export ("demo")]
[PostSnippet ("if (old != null) old.DemoComplete ();")]
void Demo ();
ProxyAttribute
這個屬性會套用以傳回值,將其標示為 Proxy 物件。 某些 Objective-C API 會傳回無法與用戶系結區別的 Proxy 物件。 這個屬性的效果是將對象標示為物件 DirectBinding
。 如需 Xamarin.Mac 中的案例,您可以查看 此 Bug 的討論。
ReleaseAttribute (Xamarin.iOS 6.0)
這可以套用至傳回型別,以指出產生器在傳回物件之前應該在 物件上呼叫 Release
。 只有當方法提供您保留的物件時,才需要這個值(而不是自動發行的物件,這是最常見的案例)
範例:
[Export ("getAndRetainObject")]
[return: Release ()]
NSObject GetAndRetainObject ();
此外,這個屬性也會傳播至產生的程式代碼,讓 Xamarin.iOS 執行時間知道它必須在從這類函式傳回時 Objective-C 保留物件。
SealedAttribute
指示產生器將產生的方法標示為密封。 如果未指定此屬性,則預設值是產生虛擬方法(虛擬方法、抽象方法或覆寫,視其他屬性的使用方式而定)。
StaticAttribute
當屬性 [Static]
套用至方法或屬性時,這會產生靜態方法或屬性。 如果未指定這個屬性,則產生器會產生實例方法或屬性。
TransientAttribute
使用這個屬性來標幟其值為暫時性的屬性,也就是iOS暫時建立但不會長時間存在的物件。 當此屬性套用至屬性時,產生器不會建立此屬性的備份欄位,這表示 Managed 類別不會保留對象的參考。
WrapAttribute
在 Xamarin.iOS/Xamarin.Mac 系結的設計中, [Wrap]
屬性是用來包裝具有強型別物件的弱型別物件。 這主要是與 Objective-C 通常宣告為 類型 id
或 NSObject
的委派物件搭配使用。 Xamarin.iOS 和 Xamarin.Mac 所使用的慣例是公開這些委派或數據源的類型, NSObject
並使用慣例 “Weak” + 所公開的名稱來命名。 id delegate
中的Objective-C屬性會公開為 NSObject WeakDelegate { get; set; }
API 合約檔案中的屬性。
但是,指派給此委派的值通常是強型別,因此我們會呈現強型別並套 [Wrap]
用 屬性,這表示如果使用者需要一些精細控制,或者如果需要訴諸低階技巧,或者他們可以使用強型別屬性來進行大部分的工作,則可以選擇使用弱型別。
範例:
[BaseType (typeof (NSObject))]
interface Demo {
[Export ("delegate"), NullAllowed]
NSObject WeakDelegate { get; set; }
[Wrap ("WeakDelegate")]
DemoDelegate Delegate { get; set; }
}
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface DemoDelegate {
[Export ("doDemo")]
void DoDemo ();
}
這是使用者如何使用委派的弱型別版本:
// The weak case, user has to roll his own
class SomeObject : NSObject {
[Export ("doDemo")]
void CallbackForDoDemo () {}
}
var demo = new Demo ();
demo.WeakDelegate = new SomeObject ();
而這就是使用者如何使用強型別版本的方式,請注意,使用者會利用 C# 的類型系統,並使用 override 關鍵詞來宣告其意圖,而且不需要手動裝飾 方法 [Export]
,因為我們已在用戶系結中執行該作業:
// This is the strong case,
class MyDelegate : DemoDelegate {
override void Demo DoDemo () {}
}
var strongDemo = new Demo ();
demo.Delegate = new MyDelegate ();
屬性的另 [Wrap]
一個用法是支持強型別的方法版本。 例如:
[BaseType (typeof (NSObject))]
interface XyzPanel {
[Export ("playback:withOptions:")]
void Playback (string fileName, [NullAllowed] NSDictionary options);
[Wrap ("Playback (fileName, options?.Dictionary")]
void Playback (string fileName, XyzOptions options);
}
[Wrap]
當屬性套用至以[Category]
屬性裝飾之型別內的方法時,您需要包含This
為第一個自變數,因為產生擴充方法。 例如:
[Wrap ("Write (This, image, options?.Dictionary, out error)")]
bool Write (CIImage image, CIImageRepresentationOptions options, out NSError error);
如果您需要成員virtual
,則預設不會virtual
產生[Wrap]
成員,您可以設定為true
選擇性isVirtual
參數。
[BaseType (typeof (NSObject))]
interface FooExplorer {
[Export ("fooWithContentsOfURL:")]
void FromUrl (NSUrl url);
[Wrap ("FromUrl (NSUrl.FromString (url))", isVirtual: true)]
void FromUrl (string url);
}
[Wrap]
也可以在屬性 getter 和 setter 中直接使用。
這可完全控制它們,並視需要調整程序代碼。
例如,請考慮使用智慧列舉的下列 API 定義:
// Smart enum.
enum PersonRelationship {
[Field (null)]
None,
[Field ("FMFather", "__Internal")]
Father,
[Field ("FMMother", "__Internal")]
Mother
}
介面定義:
// Property definition.
[Export ("presenceType")]
NSString _PresenceType { get; set; }
PersonRelationship PresenceType {
[Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType)")]
get;
[Wrap ("_PresenceType = value.GetConstant ()")]
set;
}
參數屬性
本節描述您可以套用至方法定義中參數的屬性,以及 [NullAttribute]
套用至整個屬性的 。
BlockCallback
這個屬性會套用至 C# 委派宣告中的參數類型,以通知系結器有問題的參數符合 Objective-C 區塊呼叫慣例,而且應該以這種方式封送處理。
這通常用於 在 中定義如下的 Objective-C回呼:
typedef returnType (^SomeTypeDefinition) (int parameter1, NSString *parameter2);
另請參閱: CCallback。
CCallback
此屬性會套用至 C# 委派宣告中的參數類型,以通知系結器有問題的參數符合 C ABI 函式指標呼叫慣例,而且應該以這種方式封送處理。
這通常用於 在 中定義如下的 Objective-C回呼:
typedef returnType (*SomeTypeDefinition) (int parameter1, NSString *parameter2);
另請參閱: BlockCallback。
參數
您可以在 [Params]
方法定義的最後一個數位列參數上使用 屬性,讓產生器在定義中插入「params」。 這可讓系結輕鬆允許選擇性參數。
例如,下列定義:
[Export ("loadFiles:")]
void LoadFiles ([Params]NSUrl [] files);
允許撰寫下列程式代碼:
foo.LoadFiles (new NSUrl (url));
foo.LoadFiles (new NSUrl (url1), new NSUrl (url2), new NSUrl (url3));
這有額外的優點,它不需要使用者純粹為了傳遞元素而建立數位。
PlainString
您可以使用 [PlainString]
字串參數前面的 屬性,指示係結產生器將字串當做 C 字串傳遞,而不是將參數傳遞為 NSString
。
大部分 Objective-C 的 API 會取用 NSString
參數,但少數 API 會公開 char *
API 來傳遞字串,而不是 NSString
變化。
在這些情況下使用 [PlainString]
。
例如,下列 Objective-C 宣告:
- (void) setText: (NSString *) theText;
- (void) logMessage: (char *) message;
應該像這樣系結:
[Export ("setText:")]
void SetText (string theText);
[Export ("logMessage:")]
void LogMessage ([PlainString] string theText);
RetainAttribute
指示產生器保留指定參數的參考。 產生器會提供此欄位的備份存放區,或者您可以指定名稱 ( WrapName
) 來儲存值。 這很適合用來保存傳遞為 參數 Objective-C 之 Managed 對象的參考,而且當您知道 Objective-C 只會保留這個物件的複本時。 例如,這類 SetDisplay (SomeObject)
API 會使用這個屬性,因為 SetDisplay 一次只能顯示一個物件。 如果您需要追蹤多個物件(例如,針對類似 Stack 的 API),您可以使用 [RetainList]
屬性。
語法:
public class RetainAttribute {
public RetainAttribute ();
public RetainAttribute (string wrapName);
public string WrapName { get; }
}
TransientAttribute
此屬性會套用至參數,而且只有在從 Objective-C 轉換至 C# 時才會使用。 在這些轉換期間,各種 Objective-CNSObject
參數會包裝成 物件的Managed表示法。
運行時間會取得原生對象的參考,並保留參考,直到對象的最後一個 Managed 參考消失為止,而且 GC 有機會執行。
在少數情況下,C# 運行時間務必不要保留原生對象的參考。 有時候,當基礎原生程序代碼將特殊行為附加至 參數的生命週期時,就會發生這種情況。 例如:參數的解構函式會執行一些清除動作,或處置一些寶貴的資源。
這個屬性會通知運行時間,如果可能的話,您希望在從覆寫的方法傳回時 Objective-C 處置物件。
規則很簡單:如果運行時間必須從原生物件建立新的 Managed 表示法,則函式結尾會卸除原生物件的保留計數,並清除 Managed 物件的 Handle 屬性。 這表示如果您保留 Managed 物件的參考,該參考將會變成無用的(叫用其上的方法會擲回例外狀況)。
如果未建立傳遞的物件,或對象已經有未完成的 Managed 表示法,則不會進行強制處置。
Property 屬性
NotImplementedAttribute
這個屬性可用來支援 Objective-C 在基類中引進具有 getter 之屬性的慣用語,而可變動的子類別引進 setter。
由於 C# 不支援此模型,因此基類必須同時擁有 setter 和 getter,而子類別可以使用 OverrideAttribute。
這個屬性只用於屬性 setter,並且用來支援 中的 Objective-C可變動語式。
範例:
[BaseType (typeof (NSObject))]
interface MyString {
[Export ("initWithValue:")]
IntPtr Constructor (string value);
[Export ("value")]
string Value {
get;
[NotImplemented ("Not available on MyString, use MyMutableString to set")]
set;
}
}
[BaseType (typeof (MyString))]
interface MyMutableString {
[Export ("value")]
[Override]
string Value { get; set; }
}
列舉屬性
將 NSString
常數對應至列舉值是建立更佳 .NET API 的簡單方法。 這麼做可:
- 只顯示 API 的正確值,可讓程式代碼完成更有用;
- 新增類型安全性,您無法在不正確的內容中使用另一個
NSString
常數;而且 - 允許隱藏某些常數,讓程式代碼完成顯示較短的 API 清單,而不會遺失功能。
範例:
enum NSRunLoopMode {
[DefaultEnumValue]
[Field ("NSDefaultRunLoopMode")]
Default,
[Field ("NSRunLoopCommonModes")]
Common,
[Field (null)]
Other = 1000
}
從上述系結定義中,產生器會建立 enum
本身,也會建立 *Extensions
靜態類型,其中包含列舉值與 NSString
常數之間的雙向轉換方法。 這表示即使這些常數不是 API 的一部分,仍可供開發人員使用。
範例:
// using the NSString constant in a different API / framework / 3rd party code
CallApiRequiringAnNSString (NSRunLoopMode.Default.GetConstant ());
// converting the constants from a different API / framework / 3rd party code
var constant = CallApiReturningAnNSString ();
// back into an enum value
CallApiWithEnum (NSRunLoopModeExtensions.GetValue (constant));
DefaultEnumValueAttribute
您可以使用這個屬性裝飾 一個 列舉值。 如果未知列舉值,這會成為傳回的常數。
從上述範例:
var x = (NSRunLoopMode) 99;
Call (x.GetConstant ()); // NSDefaultRunLoopMode will be used
如果沒有裝飾列舉值, NotSupportedException
則會擲回 。
ErrorDomainAttribute
錯誤碼會系結為列舉值。 它們通常會有錯誤網域,而且不一定很容易找到套用哪一個網域(或甚至存在的話)。
您可以使用這個屬性,將錯誤網域與列舉本身產生關聯。
範例:
[Native]
[ErrorDomain ("AVKitErrorDomain")]
public enum AVKitError : nint {
None = 0,
Unknown = -1000,
PictureInPictureStartFailed = -1001
}
然後,您可以呼叫擴充方法 GetDomain
,以取得任何錯誤的網域常數。
FieldAttribute
這是用於類型內常數的相同 [Field]
屬性。 它也可以在列舉內用來對應具有特定常數的值。
null
如果指定常數,可以使用 值來指定應該傳null
NSString
回的列舉值。
從上述範例:
var constant = NSRunLoopMode.NewInWatchOS3; // will be null in watchOS 2.x
Call (NSRunLoopModeExtensions.GetValue (constant)); // will return 1000
null
如果沒有值,ArgumentNullException
則會擲回 。
全域屬性
全域屬性是使用 [assembly:]
類似 [LinkWithAttribute]
的屬性修飾詞來套用,也可以用於任何位置,例如可用性屬性。
LinkWithAttribute
這是元件層級屬性,可讓開發人員指定重複使用系結連結庫所需的連結旗標,而不需要強制連結庫的取用者手動設定傳遞至連結庫的gcc_flags和額外的 mtouch 自變數。
語法:
// In properties
[Flags]
public enum LinkTarget {
Simulator = 1,
ArmV6 = 2,
ArmV7 = 4,
Thumb = 8,
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)]
public class LinkWithAttribute : Attribute {
public LinkWithAttribute ();
public LinkWithAttribute (string libraryName);
public LinkWithAttribute (string libraryName, LinkTarget target);
public LinkWithAttribute (string libraryName, LinkTarget target, string linkerFlags);
public bool ForceLoad { get; set; }
public string Frameworks { get; set; }
public bool IsCxx { get; set; }
public string LibraryName { get; }
public string LinkerFlags { get; set; }
public LinkTarget LinkTarget { get; set; }
public bool NeedsGccExceptionHandling { get; set; }
public bool SmartLink { get; set; }
public string WeakFrameworks { get; set; }
}
這個屬性會套用在元件層級,例如,這是 CorePlot 系結所使用的屬性:
[assembly: LinkWith ("libCorePlot-CocoaTouch.a", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator, Frameworks = "CoreGraphics QuartzCore", ForceLoad = true)]
當您使用 [LinkWith]
屬性時,指定的 libraryName
會內嵌到產生的元件中,讓使用者提供包含 Unmanaged 相依性的單一 DLL,以及從 Xamarin.iOS 正確取用連結庫所需的命令行旗標。
您也可以不提供 libraryName
,在此情況下 LinkWith
,屬性只能用來指定其他連結器旗標:
[assembly: LinkWith (LinkerFlags = "-lsqlite3")]
LinkWithAttribute 建構函式
這些建構函式可讓您指定要連結的連結庫,並內嵌至產生的元件、連結庫所支持的目標,以及連結庫所需的任何選擇性連結庫旗標。
請注意, LinkTarget
自變數是由 Xamarin.iOS 推斷,不需要設定。
範例:
// Specify additional linker:
[assembly: LinkWith (LinkerFlags = "-sqlite3")]
// Specify library name for the constructor:
[assembly: LinkWith ("libDemo.a");
// Specify library name, and link target for the constructor:
[assembly: LinkWith ("libDemo.a", LinkTarget.Thumb | LinkTarget.Simulator);
// Specify only the library name, link target and linker flags for the constructor:
[assembly: LinkWith ("libDemo.a", LinkTarget.Thumb | LinkTarget.Simulator, SmartLink = true, ForceLoad = true, IsCxx = true);
LinkWithAttribute.ForceLoad
屬性 ForceLoad
是用來決定連結旗標是否 -force_load
用於連結原生連結庫。 目前,這應該一律為 true。
LinkWithAttribute.Frameworks
如果系結的連結庫在任何架構上都有硬式需求(非 Foundation
和 UIKit
),您應該將 Frameworks
屬性設定為包含必要平台架構空格分隔清單的字串。 例如,如果您要繫結需要 CoreGraphics
和 CoreText
的連結庫,您會將 Frameworks
屬性設定為 "CoreGraphics CoreText"
。
LinkWithAttribute.IsCxx
如果需要使用C++編譯程式編譯產生的可執行檔,而不是使用 C 編譯程式,請將此屬性設定為 true。 如果您要系結的連結庫是以C++撰寫,請使用此連結庫。
LinkWithAttribute.LibraryName
要配套之 Unmanaged 連結庫的名稱。 這是擴展名為 「.a」 的檔案,它可以包含多個平台的物件程式代碼(例如,模擬器的 ARM 和 x86)。
舊版 Xamarin.iOS 已 LinkTarget
檢查 屬性,以判斷您支援的連結庫平臺,但現在已自動偵測到,而且 LinkTarget
會忽略 屬性。
LinkWithAttribute.LinkerFlags
字串提供 LinkerFlags
系結作者在將原生連結庫連結至應用程式時所需的任何其他連結器旗標的方式。
例如,如果原生連結庫需要 libxml2 和 zlib,您會將 LinkerFlags
字串設定為 "-lxml2 -lz"
。
LinkWithAttribute.LinkTarget
舊版 Xamarin.iOS 已 LinkTarget
檢查 屬性,以判斷您支援的連結庫平臺,但現在已自動偵測到,而且 LinkTarget
會忽略 屬性。
LinkWithAttribute.NeedsGccExceptionHandling
如果您要連結的連結庫需要 GCC 例外狀況處理連結庫,請將此屬性設定為 true (gcc_eh)
LinkWithAttribute.SmartLink
屬性 SmartLink
應該設定為 true,讓 Xamarin.iOS 判斷是否 ForceLoad
為必要。
LinkWithAttribute.WeakFrameworks
屬性 WeakFrameworks
的運作方式與 Frameworks
屬性相同,不同之處在於,在鏈接時間, -weak_framework
規範會傳遞給每個列出的架構的 gcc。
WeakFrameworks
可讓連結庫和應用程式對平臺架構進行弱式連結,以便在有可用的連結庫時選擇性地使用它們,但如果連結庫是要在較新版本的iOS上新增額外的功能,則不會對其採取硬式相依性。 如需弱式連結的詳細資訊,請參閱 Apple 關於 弱式連結的檔。
弱式連結的良好候選專案就像Frameworks
帳戶、CoreBluetooth
、、GLKit
NewsstandKit
CoreImage
和 Twitter
,因為它們只能在iOS 5中使用。
AdviceAttribute
使用此屬性為開發人員提供其他 API 的提示,這些 API 可能更方便他們使用。 例如,如果您提供 API 的強型別版本,您可以在弱型別屬性上使用這個屬性,將開發人員導向到更好的 API。
此屬性的信息會顯示在檔和工具中,以針對如何改善使用者提供建議
RequiresSuperAttribute
這是屬性的特殊 [Advice]
子類別,可用來提示開發人員覆寫方法 需要 呼叫基底 (覆寫) 方法。
這會對應至 clang
__attribute__((objc_requires_super))
ZeroCopyStringsAttribute
僅適用於 Xamarin.iOS 5.4 和更新。
這個屬性會指示產生器,這個特定連結庫的系結(如果套用 [assembly:]
為 ),或類型應該使用快速的零複製字串封送處理。 這個屬性相當於將命令行選項 --zero-copy
傳遞至產生器。
針對字串使用零複製時,產生器會有效地使用與 Objective-C 取用字串相同的 C# 字串,而不會產生新 NSString
物件的建立,並避免將數據從 C# 字串 Objective-C 複製到字串。 使用零複製字串的唯一缺點是,您必須確定您包裝的任何字串屬性都恰好標示為 retain
或 copy
已 [DisableZeroCopy]
設定屬性。 這是必要專案,因為零複製字串的句柄是在堆疊上配置,而且在函式傳回時無效。
範例:
[ZeroCopyStrings]
[BaseType (typeof (NSObject))]
interface MyBinding {
[Export ("name")]
string Name { get; set; }
[Export ("domain"), NullAllowed]
string Domain { get; set; }
[DisablZeroCopy]
[Export ("someRetainedNSString")]
string RetainedProperty { get; set; }
}
您也可以在元件層級套用 屬性,而且它會套用至元件的所有類型:
[assembly:ZeroCopyStrings]
強型別字典
透過 Xamarin.iOS 8.0,我們引進了輕鬆建立包裝 NSDictionaries
的強型別類別的支援。
雖然一直可以搭配手動 API 使用 DictionaryContainer 數據類型,但現在執行這項操作會比較簡單。 如需詳細資訊,請參閱 顯示強型別。
StrongDictionary
將此屬性套用至介面時,產生器會產生與衍生自 DictionaryContainer 之介面同名的類別,並將介面中定義的每個屬性轉換成字典的強型別 getter 和 setter。
這會自動產生可從現有 NSDictionary
或已建立新實例化的類別。
這個屬性會採用一個參數,這個類別的名稱包含用來存取字典上元素的索引鍵。 根據預設,介面中具有 屬性的每個屬性都會查閱指定類型中具有後綴 「Key」 之名稱的成員。
例如:
[StrongDictionary ("MyOptionKeys")]
interface MyOption {
string Name { get; set; }
nint Age { get; set; }
}
[Static]
interface MyOptionKeys {
// In Objective-C this is "NSString *MYOptionNameKey;"
[Field ("MYOptionNameKey")]
NSString NameKey { get; }
// In Objective-C this is "NSString *MYOptionAgeKey;"
[Field ("MYOptionAgeKey")]
NSString AgeKey { get; }
}
在上述案例中,類別 MyOption
會產生字串屬性,該屬性 Name
會使用 MyOptionKeys.NameKey
做為字典中的索引鍵來擷取字串。 而且會使用 MyOptionKeys.AgeKey
作為字典中的索引鍵來擷取 NSNumber
包含 int 的 。
如果您想要使用不同的索引鍵,可以在屬性上使用 export 屬性,例如:
[StrongDictionary ("MyColoringKeys")]
interface MyColoringOptions {
[Export ("TheName")] // Override the default which would be NameKey
string Name { get; set; }
[Export ("TheAge")] // Override the default which would be AgeKey
nint Age { get; set; }
}
[Static]
interface MyColoringKeys {
// In Objective-C this is "NSString *MYColoringNameKey"
[Field ("MYColoringNameKey")]
NSString TheName { get; }
// In Objective-C this is "NSString *MYColoringAgeKey"
[Field ("MYColoringAgeKey")]
NSString TheAge { get; }
}
強字典類型
定義支援 StrongDictionary
下列資料類型:
C# 介面類型 | NSDictionary 記憶體類型 |
---|---|
bool |
Boolean 儲存在 NSNumber |
列舉值 | 儲存在中的整數 NSNumber |
int |
儲存在中的32位整數 NSNumber |
uint |
儲存在中的32位無符號整數 NSNumber |
nint |
NSInteger 儲存在 NSNumber |
nuint |
NSUInteger 儲存在 NSNumber |
long |
儲存在中的64位整數 NSNumber |
float |
儲存為的32位整數 NSNumber |
double |
儲存為的64位整數 NSNumber |
NSObject 和子類別 |
NSObject |
NSDictionary |
NSDictionary |
string |
NSString |
NSString |
NSString |
C# Array NSObject |
NSArray |
列舉的 C# Array |
NSArray 包含 NSNumber 值 |