Поделиться через


Справочник по типам привязки

В этом документе описывается список атрибутов, которые можно использовать для аннотации файлов контракта API для управления привязкой и созданным кодом.

Контракты API Xamarin.iOS и Xamarin.Mac записываются в C# главным образом в виде определений интерфейсов, определяющих способ Objective-C поверхности кода на C#. Процесс включает в себя сочетание объявлений интерфейса, а также некоторые основные определения типов, которым может потребоваться контракт API. Общие сведения о типах привязок см. в наших вспомогательных библиотеках привязкиObjective-C.

Определения типов

Синтаксис

[BaseType (typeof (BTYPE))
interface MyType : [Protocol1, Protocol2] {
     IntPtr Constructor (string foo);
}

Каждый интерфейс в определении контракта, имеющий [BaseType] атрибут, объявляет базовый тип для созданного объекта. В приведенном выше объявлении MyType будет создан тип C# класса, который привязывается к типу Objective-C , который называется MyType.

Если указать какие-либо типы после имени типа (в примере выше Protocol1 иProtocol2) с помощью синтаксиса наследования интерфейса содержимое этих интерфейсов будет вложено так, как если бы они были частью контракта.MyType Способ применения протокола Xamarin.iOS заключается в том, что тип принимает протокол путем встраивание всех методов и свойств, объявленных в протоколе в сам тип.

Ниже показано, как Objective-C объявление для UITextField этого будет определено в контракте Xamarin.iOS:

@interface UITextField : UIControl <UITextInput> {

}

Будет написан так же, как контракт API C#:

[BaseType (typeof (UIControl))]
interface UITextField : UITextInput {
}

Вы можете управлять многими другими аспектами создания кода, применяя другие атрибуты к интерфейсу, а также настраивая [BaseType] атрибут.

Создание событий

Одна из функций проектирования API Xamarin.iOS и Xamarin.Mac заключается в том, что мы сопоставляем 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# для той же функции выглядит так, как с помощью лямбда-кодов:

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 значение, в этом случае лямбда-синтаксис позволяет нам просто возвращать значение из MakeDecision функции.

Генератор привязок поддерживает создание событий и свойств, которые связывают класс, как UIScrollView с его UIScrollViewDelegate (хорошо называются классом Model), это делается путем аннотирования [BaseType] определения с Events помощью и Delegates параметров (описанных ниже). Помимо аннотирования [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 которое не соответствует этому соглашению.

Например, в следующем случае мы сопоставляем тип с NSUrlConnectionтипомObjective-CNSURLConnection, так как рекомендации по проектированию платформа .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 и Delegates свойства атрибута [BaseType] .

Эти свойства всегда задаются вместе и должны иметь одинаковое количество элементов и храниться в синхронизации. Массив 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 классе, поэтому его необходимо использовать в сочетании с Events свойствами и Delegates свойствами.

В следующем примере показано, как это используется UIActionSheet в Xamarin.iOS:

[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);
}

НазначеноDefaultCtorAttribute

Если этот атрибут применяется к определению интерфейса, он создаст [DesignatedInitializer] атрибут по умолчанию (созданного) конструктора, который сопоставляется с селектором init .

DisableDefaultCtorAttribute

Если этот атрибут применяется к определению интерфейса, он не позволит генератору создавать конструктор по умолчанию.

Используйте этот атрибут, если требуется инициализировать объект с помощью одного из других конструкторов класса.

PrivateDefaultCtorAttribute

Если этот атрибут применяется к определению интерфейса, он помечает конструктор по умолчанию как закрытый. Это означает, что вы по-прежнему можете создать экземпляр объекта этого класса внутри файла расширения, но он просто не будет доступен пользователям вашего класса.

CategoryAttribute

Используйте этот атрибут для определения типа, чтобы привязать Objective-C категории и предоставить их как методы расширения C# для зеркального отображения Objective-C возможностей.

Категории — это механизм, используемый Objective-C для расширения набора методов и свойств, доступных в классе. На практике они используются для расширения функциональных возможностей базового класса (например NSObject, при связывании определенной платформы), что делает свои методы доступными, но только если новая платформа связана UIKit. В некоторых других случаях они используются для упорядочивания функций в классе по функциям. Они похожи на методы расширения C#.

Это то, что категория будет выглядеть следующим образом:Objective-C

@interface UIView (MyUIViewExtension)
-(void) makeBackgroundRed;
@end

Приведенный выше пример найден в библиотеке, которая расширит экземпляры UIView с помощью метода makeBackgroundRed.

Для привязки [Category] этих атрибутов можно использовать в определении интерфейса. При использовании [Category] атрибута значение атрибута [BaseType] изменяется от использования, чтобы указать базовый класс для расширения, чтобы быть типом для расширения.

Ниже показано, как UIView расширения привязаны и преобразуются в методы расширения C#:

[BaseType (typeof (UIView))]
[Category]
interface MyUIViewExtension {
    [Export ("makeBackgroundRed")]
    void MakeBackgroundRed ();
}

В приведенном выше примере будет создан MyUIViewExtension класс, содержащий MakeBackgroundRed метод расширения. Это означает, что теперь вы можете вызывать MakeBackgroundRed любой UIView подкласс, предоставляя вам те же функции, что и вы.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);
}

Мы будем выдавать предупреждение (BI1117) всякий раз, когда мы найдем [Static] член внутри [Category] определения. Если вы действительно хотите иметь [Static] элементы внутри [Category] определений, вы можете замолчать предупреждение с помощью [Category (allowStaticMembers: true)] или декорирования элемента или [Category] определения интерфейса.[Internal]

StaticAttribute

Если этот атрибут применяется к классу, он просто создаст статический класс, который не является производным от NSObject, поэтому [BaseType] атрибут игнорируется. Статические классы используются для размещения общедоступных переменных C, которые необходимо предоставить.

Например:

[Static]
interface CBAdvertisement {
    [Field ("CBAdvertisementDataServiceUUIDsKey")]
    NSString DataServiceUUIDsKey { get; }

Будет создавать класс C# со следующим API:

public partial class CBAdvertisement  {
    public static NSString DataServiceUUIDsKey { get; }
}

Определения протокола и модели

Модели обычно используются реализацией протокола. Они отличаются тем, что среда выполнения будет регистрироваться только с Objective-C помощью методов, которые фактически были перезаписаны. В противном случае метод не будет зарегистрирован.

Это означает, что при подклассе класса, помеченного флагом с помощью ModelAttributeбазового метода, не следует вызывать базовый метод. Вызов этого метода вызовет следующее исключение: Foundation.You_Should_Not_Call_base_In_This_Method. Вы должны реализовать все поведение в подклассе для любых методов, которые вы переопределяете.

AbstractAttribute

По умолчанию члены, которые являются частью протокола, не являются обязательными. Это позволяет пользователям создавать подкласс Model объекта, просто производный от класса в C# и переопределяя только те методы, которые они заботят. 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, значение по умолчанию, возвращаемое в данном случае, будет 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; }
}

Этот атрибут при указании метода, возвращающего значение в классе модели, будет указывать генератору возвращать значение указанного параметра, если пользователь не предоставил собственный метод или лямбда-код.

Пример:

[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 лямбда-метод, возвращаемое значение будет значением, переданным в параметре хода выполнения.

См. также: [NoDefaultValue][DefaultValue]

ИгнорируетсяInDelegateAttribute

Иногда не следует предоставлять свойство события или делегата из класса 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);
}

ДелегатNameAttribute

Этот атрибут используется в методах модели, возвращающих значения, чтобы задать имя используемой подписи делегата.

Пример:

[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);

ДелегатApiNameAttribute

Этот атрибут используется для изменения имени свойства, созданного в классе узла. Иногда это полезно, если имя метода класса FooDelegate имеет смысл для класса Делегата, но будет выглядеть странно в классе узла как свойство.

Кроме того, это действительно полезно (и необходимо), если у вас есть два или более методов перегрузки, которые имеют смысл сохранить их как в классе 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 соглашении является то, что первый параметр в классе делегата является экземпляром объекта отправителя), необходимо указать имя, которое необходимо указать для созданного класса 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 контракта среда выполнения создаст специальный код, который будет вызывать методы только в классе, если пользователь перезаписывает метод в классе. Этот атрибут обычно применяется ко всем API, которые упаковывают класс делегата Objective-C .

Среда выполнения также создаст Objective-C класс, соответствующий имени соответствующего протокола.

Можно настроить имя Objective-C класса двумя способами:

  1. Параметр:AutoGeneratedName = true

    [Model (AutoGeneratedName = true)]
    

    Это приведет к созданию уникального Objective-C имени среды выполнения для типа. Имя в настоящее время основано на имени сборки и полном имени типа модели (это может измениться в будущем).

  2. Явно указывая имя:

    [Model (Name = "CustomName")]
    

Рекомендуется использовать AutoGeneratedName = true. В .NET имя всегда создается (если он явно не указан как в 2. выше), а AutoGeneratedName свойство больше не существует.

NoDefaultValueAttribute

Указывает, что метод в модели не предоставляет возвращаемое значение по умолчанию.

Это работает с Objective-C средой выполнения, отвечая на Objective-C false запрос среды выполнения, чтобы определить, реализован ли указанный селектор в этом классе.

[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. Если вы хотите использовать MyProtocol в API, вам потребуется сделать следующее:

[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)

но при использовании интерфейса протокола необходимо добавить [экспорт]. Интегрированная среда разработки добавит ее через автозавершение при добавлении, начиная с переопределения.

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 байтам). Это свойство можно использовать для декорирования метода получения и использования значения выравнивания. Обычно это используется с типами и OpenTK.Matrix4 типами при интеграции с OpenTK.Vector4 Objective-C API.

Пример:

public interface GLKBaseEffect {
    [Export ("constantColor")]
    Vector4 ConstantColor { [Align (16)] get; set;  }
}

Внешний видAttribute

Атрибут [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] Используйте методы и свойства для оболочки вызова метода к методу в объектеNSAutoReleasePool.

В Objective-C некоторых методах, возвращающих значения, добавленные в значение по умолчанию NSAutoReleasePool. По умолчанию они будут переходить к потоку NSAutoReleasePool, но так как Xamarin.iOS также сохраняет ссылку на объекты до тех пор, пока управляемый объект живет, вам может не потребоваться сохранить дополнительную ссылку на NSAutoReleasePool нее, которая будет использоваться только до тех пор, пока поток не вернется к следующему потоку, или вернуться к основному циклу.

Этот атрибут применяется например к тяжелым свойствам (например UIImage.FromFile), возвращающим объекты, добавленные в значение по умолчанию NSAutoReleasePool. Без этого атрибута изображения будут храниться до тех пор, пока поток не вернул элемент управления в основной цикл. Uf ваш поток был каким-то фоновым загрузчиком, который всегда жив и ждет работы, изображения никогда не будут выпущены.

ForcedTypeAttribute

Используется [ForcedTypeAttribute] для принудительного создания управляемого типа, даже если возвращаемый неуправляемый объект не соответствует типу, описанному в определении привязки.

Это полезно, если тип, описанный в заголовке, не соответствует возвращаемого типа собственного метода, например, принимает следующее Objective-C определение из NSURLSession:

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request

Очевидно, что он вернет NSURLSessionDownloadTask экземпляр, но все же возвращает NSURLSessionTask объект , который является суперклассом и таким образом не преобразуется в 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] привязку NSNumberNSValue и NSString(перечисления) в более точные типы C#. Атрибут можно использовать для создания более точного, более точного API .NET через собственный API.

Методы можно декорировать (при возвращаемом значении), параметрами и свойствами BindAs. Единственное ограничение заключается в том, что член не должен находиться внутри или [Model] в интерфейсе[Protocol].

Например:

[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
  • двойной точности
  • с плавающей запятой
  • short
  • INT
  • длинный
  • sbyte
  • ushort
  • uint
  • ulong
  • nfloat
  • nint
  • nuint
  • Перечисления

NSString

[BindAs]работает в константе NSString с перечислением, чтобы можно было создать лучший API .NET, например:

[BindAs (typeof (CAScroll))]
[Export ("supportedScrollMode")]
NSString SupportedScrollMode { get; set; }

Выходные данные:

[Export ("supportedScrollMode")]
CAScroll SupportedScrollMode { get; set; }

Мы будем обрабатывать enum<>NSString преобразование только в том случае, если предоставленный тип [BindAs] перечисления поддерживается константой 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 из них, и в возврате вы получите массив CAScroll? , созданный с помощью значений возвращаемого NSArray содержащего NSStrings.

BindAttribute

Атрибут [Bind] имеет два использования, когда применяется к объявлению метода или свойства, а другой при применении к отдельному методу получения или задания в свойстве.

Если используется для метода или свойства, [Bind] эффект атрибута заключается в создании метода, вызывающего указанный селектор. Но полученный созданный метод не украшен [Export] атрибутом, что означает, что он не может участвовать в переопределении метода. Обычно это используется в сочетании с атрибутом [Target] для реализации Objective-C методов расширения.

Например:

public interface UIView {
    [Bind ("drawAtPoint:withFont:")]
    SizeF DrawString ([Target] string str, CGPoint point, UIFont font);
}

При использовании в методе получения или задания [Bind] атрибут используется для изменения значений по умолчанию, выводимых генератором кода при создании имен селектора получения и задания Objective-C для свойства. По умолчанию при флаге свойства с именем fooBarгенератор создаст fooBar экспорт для метода получения и setFooBar: задания. В некоторых случаях Objective-C не следует этому соглашению, обычно они изменяют имя 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] в конечной сборке платформы. Это поможет интегрированной среде разработки указать, какой конструктор должен использоваться в подклассах.

Это должно сопоставляться с Objective-Cиспользованием /clang.__attribute__((objc_designated_initializer))

DisableZeroCopyAttribute

Этот атрибут применяется к строковым параметрам или строковым свойствам и указывает генератору кода не использовать маршалинг строк нулевого копирования для этого параметра, а вместо этого создать новый экземпляр NSString из строки C#. Этот атрибут требуется только в строках, если вы указываете генератору использовать маршалинг строк нулевого копирования с помощью --zero-copy параметра командной строки или настройки атрибута ZeroCopyStringsAttributeуровня сборки.

Это необходимо в случаях, когда свойство объявляется retain как Objective-C свойство или assign свойство вместо copy свойства. Обычно они происходят в сторонних библиотеках, которые были неправильно оптимизированы разработчиками. Как правило, или assign NSString свойства неверны, retain так как NSMutableString или производные от пользователя классы 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. Для методов параметр передается подробно в созданный код для свойств, создается метод получения и задания экспорта на основе базового объявления (см. раздел о [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, но при необходимости их можно вручную привязать.

Пример:

[Static]
interface CameraEffects {
     [Field ("kCameraEffectsZoomFactorKey", "CameraLibrary")]
     NSString ZoomFactorKey { get; }
}

InternalAttribute

Атрибут [Internal] может применяться к методам или свойствам, и он влияет на перетаскивание созданного кода с internal помощью ключевого слова C#, что делает код доступным только для кода в созданной сборке. Это обычно используется для скрытия 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] . Это полезно, если поле является статической переменной потока.

МаршалNativeExceptions (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 которые будут получать данные из словаря NSNotification.UserInfo , используя ключи ScreenXKey , и ScreenYKey соответственно применить соответствующие преобразования. Атрибут [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 создаст проверку, которая вызовет исключение ArgumentNullException , если присвоено nullзначение.

Например:

// 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

Указывает генератору привязки вызвать указанное свойство из этого класса, чтобы получить из него значение.

Обычно это свойство используется для обновления кэша, указывающего на ссылки на объекты, на которые ссылается граф объектов. Обычно он отображается в коде с операциями, такими как Add/Remove. Этот метод используется так, чтобы после добавления или удаления элементов внутренний кэш был обновлен, чтобы обеспечить сохранение управляемых ссылок на объекты, которые фактически используются. Это возможно, так как средство привязки создает резервное поле для всех ссылочных объектов в данной привязке.

Пример:

[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

Этот атрибут применяется к возвращаемым значениям, чтобы пометить их как прокси-объекты. Некоторые Objective-C API возвращают прокси-объекты, которые не могут отличаться от пользовательских привязок. Эффект этого атрибута заключается в том, чтобы пометить объект как DirectBinding объект. Для сценария в Xamarin.Mac можно просмотреть обсуждение этой ошибки.

ReleaseAttribute (Xamarin.iOS 6.0)

Это можно применить к возвращаемым типам, чтобы указать, что генератор должен вызывать Release объект перед возвратом. Это необходимо только в том случае, если метод предоставляет сохраненный объект (в отличие от автоматического запуска объекта, который является наиболее распространенным сценарием).

Пример:

[Export ("getAndRetainObject")]
[return: Release ()]
NSObject GetAndRetainObject ();

Кроме того, этот атрибут распространяется на созданный код, чтобы среда выполнения Xamarin.iOS знала, что он должен хранить объект после возвращения Objective-C из такой функции.

SealedAttribute

Указывает генератору пометить созданный метод как запечатанный. Если этот атрибут не указан, по умолчанию создается виртуальный метод (либо виртуальный метод, абстрактный метод, либо переопределение в зависимости от того, как используются другие атрибуты).

StaticAttribute

[Static] При применении атрибута к методу или свойству создается статический метод или свойство. Если этот атрибут не указан, генератор создает метод экземпляра или свойство.

TransientAttribute

Используйте этот атрибут для флага свойств, значения которых являются временными, то есть объекты, которые временно создаются iOS, но не являются длительными. Если этот атрибут применяется к свойству, генератор не создает резервное поле для этого свойства, что означает, что управляемый класс не сохраняет ссылку на объект.

WrapAttribute

В конструкторе привязок [Wrap] Xamarin.iOS/Xamarin.Mac атрибут используется для упаковки слабо типизированного объекта с строго типизированным объектом. Это происходит в основном с Objective-C объектами делегатов, которые обычно объявляются как тип id или NSObject. Соглашение, используемое Xamarin.iOS и Xamarin.Mac, заключается в том, чтобы предоставлять эти делегаты или источники данных как тип NSObject и называться с помощью соглашения "Слабый" + имя, которое предоставляется. Свойство 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#и использует ключевое слово переопределения для объявления намерения и не требуется вручную декорировать метод, [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);

Элементы, созданные [Wrap] не virtual по умолчанию, если вам нужен virtual элемент, можно задать необязательный isVirtual true параметр.

[BaseType (typeof (NSObject))]
interface FooExplorer {
    [Export ("fooWithContentsOfURL:")]
    void FromUrl (NSUrl url);

    [Wrap ("FromUrl (NSUrl.FromString (url))", isVirtual: true)]
    void FromUrl (string url);
}

[Wrap] также можно использовать непосредственно в методах получения свойств и методах задания. Это позволяет иметь полный контроль над ними и настраивать код по мере необходимости. Например, рассмотрим следующее определение 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);

СохранитьAttribute

Указывает генератору сохранить ссылку на указанный параметр. Генератор предоставит резервное хранилище для этого поля или укажите имя (the WrapName) для хранения значения. Это полезно для хранения ссылки на управляемый объект, передаваемый в качестве параметра Objective-C , и когда вы знаете, что Objective-C будет хранить только эту копию объекта. Например, API, например SetDisplay (SomeObject) , будет использовать этот атрибут, так как, скорее всего, SetDisplay может отображать только один объект за раз. Если необходимо отслеживать несколько объектов (например, для API stack-like), вы будете использовать [RetainList] атрибут.

Синтаксис

public class RetainAttribute {
    public RetainAttribute ();
    public RetainAttribute (string wrapName);
    public string WrapName { get; }
}

TransientAttribute

Этот атрибут применяется к параметрам и используется только при переходе с Objective-C C#. Во время этих переходов различные Objective-CNSObject параметры упаковываются в управляемое представление объекта.

Среда выполнения будет принимать ссылку на собственный объект и сохранять ссылку до тех пор, пока не будет удалена последняя управляемая ссылка на объект, и GC имеет возможность запуститься.

В некоторых случаях важно, чтобы среда выполнения C# не сохраняла ссылку на собственный объект. Иногда это происходит, когда базовый машинный код привязывает специальное поведение к жизненному циклу параметра. Например, деструктор для параметра выполнит некоторые действия очистки или удаляет драгоценный ресурс.

Этот атрибут сообщает среде выполнения, что требуется, чтобы объект был удален, если это возможно, при возврате обратно Objective-C из метода перезаписи.

Правило простое: если среде выполнения пришлось создать новое управляемое представление из собственного объекта, то в конце функции будет удалено количество хранения для собственного объекта, а свойство Handle управляемого объекта будет удалено. Это означает, что если вы сохранили ссылку на управляемый объект, эта ссылка станет бесполезной (вызов методов в ней вызовет исключение).

Если переданный объект не был создан, или если уже было выдающееся управляемое представление объекта, принудительное удаление не происходит.

Атрибуты свойства

NotImplementedAttribute

Этот атрибут используется для поддержки Objective-C идиома, в котором свойство с методом getter представлено в базовом классе, а подкласс мутируемый вводит метод задания.

Так как C# не поддерживает эту модель, базовый класс должен иметь как метод задания, так и метод получения, а подкласс может использовать OverrideAttribute.

Этот атрибут используется только в наборах свойств и используется для поддержки мутируемой идиомы.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 констант с значениями перечисления — это простой способ создания лучшего API .NET. Оно делает следующее.

  • позволяет использовать завершение кода, показывая только правильные значения для 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 внедряется в результирующую сборку, позволяя пользователям отправлять одну библиотеку 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 флаг ссылки для связывания собственной библиотеки. На данный момент это всегда должно быть верно.

LinkWithAttribute.Frameworks

Если привязанная библиотека имеет жесткое требование для любой платформы (кроме Foundation и UIKit), необходимо задать Frameworks для свойства строку, содержащую список необходимых платформ с разделителями пространства. Например, если вы привязывают библиотеку, которая требуется CoreGraphics , и CoreTextприсвойте свойству Frameworks значение "CoreGraphics CoreText".

LinkWithAttribute.IsCxx

Задайте для этого свойства значение true, если результирующий исполняемый файл необходимо скомпилировать с помощью компилятора C++, а не по умолчанию, который является компилятором C. Используйте это, если библиотека, которую вы являетесь привязкой, была написана на языке C++.

LinkWithAttribute.LibraryName

Имя неуправляемой библиотеки для пакета. Это файл с расширением .a" и может содержать код объекта для нескольких платформ (например, ARM и x86 для симулятора).

Более ранние версии Xamarin.iOS проверили LinkTarget свойство, чтобы определить поддерживаемую платформу, но теперь она обнаружена автоматически, и LinkTarget свойство игнорируется.

LinkWithAttribute.LinkerFlags

Строка LinkerFlags позволяет авторам привязки указывать все дополнительные флаги компоновщика, необходимые при связывании собственной библиотеки с приложением.

Например, если для собственной библиотеки требуется libxml2 и zlib, необходимо задать LinkerFlags для "-lxml2 -lz"строки значение .

LinkWithAttribute.LinkTarget

Более ранние версии Xamarin.iOS проверили LinkTarget свойство, чтобы определить поддерживаемую платформу, но теперь она обнаружена автоматически, и LinkTarget свойство игнорируется.

LinkWithAttribute.NeedsGccExceptionHandling

Задайте для этого свойства значение true, если для связывания библиотеки требуется библиотека обработки исключений GCC (gcc_eh)

Свойство SmartLink должно иметь значение true, чтобы разрешить Xamarin.iOS определить, является ли ForceLoad это обязательным или нет.

LinkWithAttribute.WeakFrameworks

Свойство WeakFrameworks работает так же, как Frameworks свойство, за исключением того, -weak_framework что в режиме связи описатель передается в gcc для каждой из перечисленных платформ.

WeakFrameworks позволяет библиотекам и приложениям слабо связываться с платформами, чтобы они могли при необходимости использовать их, если они доступны, но не принимают жесткой зависимости от них, что полезно, если библиотека предназначена для добавления дополнительных функций в более новых версиях iOS. Дополнительные сведения о слабом связывании см. в документации Apple по слабым связываниям.

Хорошие кандидаты для слабой компоновки будут Frameworks такими как Accounts, CoreBluetooth, CoreImage, GLKitNewsstandKit и Twitter так как они доступны только в iOS 5.

СоветAttribute

Используйте этот атрибут, чтобы дать разработчикам указание о других API, которые могут быть удобнее для них использовать. Например, если вы предоставляете строго типизированную версию API, этот атрибут можно использовать для слабо типизированного атрибута, чтобы направить разработчика к лучшему API.

Сведения из этого атрибута отображаются в документации и средствах для предоставления пользовательских предложений по улучшению

ТребуетСяSuperAttribute

Это специализированный подкласс атрибута[Advice], который можно использовать для указания разработчику, что переопределение метода требует вызова базового (переопределенного) метода.

Это соответствует clang __attribute__((objc_requires_super))

ZeroCopyStringsAttribute

Доступна только в Xamarin.iOS 5.4 и более поздней версии.

Этот атрибут указывает генератору, что привязка для данной конкретной библиотеки (при применении к [assembly:]) или типу должна использовать быстрый маршалинг строк нулевого копирования. Этот атрибут эквивалентен передаче параметра --zero-copy командной строки генератору.

При использовании нулевого копирования для строк генератор фактически использует ту же строку C#, что и строка Objective-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.

Хотя всегда можно было использовать тип данных DictionaryContainer вместе с ручным API, это гораздо проще сделать. Дополнительные сведения см. в разделе "Строгое отображение типов".

StrongDictionary

При применении этого атрибута к интерфейсу генератор создает класс с тем же именем, что и интерфейс, производный от DictionaryContainer , и преобразует каждое свойство, определенное в интерфейсе, в строго типизированный метод получения и задание словаря.

Это автоматически создает класс, который можно создать из существующего NSDictionary или созданного.

Этот атрибут принимает один параметр, имя класса, содержащего ключи, используемые для доступа к элементам словаря. По умолчанию каждое свойство в интерфейсе с атрибутом будет искать член в указанном типе для имени с суффиксом "Ключ".

Например:

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

Если вы хотите использовать другой ключ, можно использовать атрибут экспорта для свойства, например:

[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# ArrayNSObject NSArray
C# Array перечислений NSArrayNSNumber содержащие значения