Guida di riferimento ai tipi di associazione

Questo documento descrive l'elenco degli attributi che è possibile usare per annotare i file del contratto API per guidare l'associazione e il codice generato

I contratti API Xamarin.iOS e Xamarin.Mac vengono scritti in C# principalmente come definizioni di interfaccia che definiscono il modo Objective-C in cui il codice viene visualizzato in C#. Il processo prevede una combinazione di dichiarazioni di interfaccia e alcune definizioni di tipo di base che il contratto API potrebbe richiedere. Per un'introduzione ai tipi di binding, vedere la guida complementare Alle librerie di bindingObjective-C.

Definizioni di tipo

Sintassi:

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

Ogni interfaccia nella definizione del contratto con l'attributo [BaseType] dichiara il tipo di base per l'oggetto generato. Nella dichiarazione precedente verrà generato un MyType tipo C# di classe che esegue l'associazione a un Objective-C tipo denominato MyType.

Se si specificano tipi dopo il nometipo (nell'esempio precedente Protocol1 e Protocol2) usando la sintassi di ereditarietà dell'interfaccia, il contenuto di tali interfacce verrà inlinede come se fossero parte del contratto per MyType. Il modo in cui Xamarin.iOS espone che un tipo adotta un protocollo consiste nell'inlining di tutti i metodi e le proprietà dichiarati nel protocollo stesso.

Di seguito viene illustrato come definire la Objective-C dichiarazione per UITextField in un contratto Xamarin.iOS:

@interface UITextField : UIControl <UITextInput> {

}

Verrà scritto come contratto API C#:

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

È possibile controllare molti altri aspetti della generazione di codice applicando altri attributi all'interfaccia e configurando l'attributo [BaseType] .

Generazione di eventi

Una funzionalità della progettazione dell'API Xamarin.iOS e Xamarin.Mac consiste nel eseguire il mapping Objective-C delle classi delegate come eventi e callback C#. Gli utenti possono scegliere in base all'istanza se vogliono adottare il Objective-C modello di programmazione assegnando a proprietà come Delegate un'istanza di una classe che implementa i vari metodi che il Objective-C runtime chiamerebbe o scegliendo gli eventi e le proprietà in stile C#.

Di seguito è riportato un esempio di come usare il Objective-C modello:

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

Nell'esempio precedente è possibile notare che è stato scelto di sovrascrivere due metodi, una notifica che ha avuto luogo un evento di scorrimento e la seconda che è un callback che deve restituire un valore booleano che indica se deve scorrere verso l'alto scrollView o meno.

Il modello C# consente all'utente della libreria di ascoltare le notifiche usando la sintassi degli eventi C# o la sintassi della proprietà per associare i callback previsti per restituire valori.

Questo è il modo in cui il codice C# per la stessa funzionalità è simile all'uso delle espressioni 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 ();
}

Poiché gli eventi non restituiscono valori (hanno un tipo restituito void), è possibile connettere più copie. Non ShouldScrollToTop è un evento, ma è una proprietà con il tipo UIScrollViewCondition con questa firma:

public delegate bool UIScrollViewCondition (UIScrollView scrollView);

Restituisce un bool valore, in questo caso la sintassi lambda consente di restituire semplicemente il valore dalla MakeDecision funzione.

Il generatore di associazioni supporta la generazione di eventi e proprietà che collegano una classe come UIScrollView con la relativa UIScrollViewDelegate (chiamare bene queste classi Model), questa operazione viene eseguita annotando la [BaseType] definizione con i Events parametri e Delegates (descritti di seguito). Oltre ad annotare con [BaseType] questi parametri è necessario informare il generatore di altri componenti.

Per gli eventi che accettano più di un parametro (nella Objective-C convenzione è che il primo parametro in una classe delegate è l'istanza dell'oggetto mittente) è necessario specificare il nome che si desidera che la classe generata EventArgs sia. Questa operazione viene eseguita con l'attributo [EventArgs] nella dichiarazione del metodo nella classe Model. Ad esempio:

[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
    [Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
    void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}

La dichiarazione precedente genererà una UIImagePickerImagePickedEventArgs classe che deriva da EventArgs e comprime entrambi i parametri, UIImage e NSDictionary. Il generatore produce questo:

public partial class UIImagePickerImagePickedEventArgs : EventArgs {
    public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
    public UIImage Image { get; set; }
    public NSDictionary EditingInfo { get; set; }
}

Espone quindi quanto segue nella UIImagePickerController classe :

public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }

I metodi del modello che restituiscono un valore sono associati in modo diverso. Questi richiedono sia un nome per il delegato C# generato (la firma per il metodo) sia un valore predefinito da restituire nel caso in cui l'utente non fornisca un'implementazione. Ad esempio, la ShouldScrollToTop definizione è la seguente:

[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIScrollViewDelegate {
    [Export ("scrollViewShouldScrollToTop:"), DelegateName ("UIScrollViewCondition"), DefaultValue ("true")]
    bool ShouldScrollToTop (UIScrollView scrollView);
}

Il codice precedente creerà un UIScrollViewCondition delegato con la firma illustrata in precedenza e, se l'utente non fornisce un'implementazione, il valore restituito sarà true.

Oltre all'attributo [DefaultValue] , è anche possibile usare l'attributo [DefaultValueFromArgument] che indirizza il generatore a restituire il valore del parametro specificato nella chiamata o il [NoDefaultValue] parametro che indica al generatore che non esiste alcun valore predefinito.

BaseTypeAttribute

Sintassi:

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

Utilizzare la Name proprietà per controllare il nome a cui questo tipo verrà associato nel Objective-C mondo. Viene in genere usato per assegnare al tipo C# un nome conforme alle linee guida per la progettazione di .NET Framework, ma che esegue il mapping a un nome in Objective-C che non segue tale convenzione.

Ad esempio, nel caso seguente viene eseguito il mapping del Objective-CNSURLConnection tipo a NSUrlConnection, poiché le linee guida per la progettazione di .NET Framework usano "URL" anziché "URL":

[BaseType (typeof (NSObject), Name="NSURLConnection")]
interface NSUrlConnection {
}

Il nome specificato viene usato come valore per l'attributo generato [Register] nell'associazione. Se Name non viene specificato, il nome breve del tipo viene usato come valore per l'attributo [Register] nell'output generato.

BaseType.Events e BaseType.Delegates

Queste proprietà vengono usate per guidare la generazione di eventi in stile C#nelle classi generate. Vengono usati per collegare una determinata classe alla relativa Objective-C classe delegato. Si verificano molti casi in cui una classe usa una classe delegato per inviare notifiche ed eventi. Ad esempio, un BarcodeScanner oggetto avrebbe una classe complementare BardodeScannerDelegate . La BarcodeScanner classe ha in genere una Delegate proprietà a cui assegnare un'istanza di BarcodeScannerDelegate , mentre funziona, potrebbe essere necessario esporre agli utenti un'interfaccia evento di tipo C#, e in questi casi si userebbero le Events proprietà e Delegates dell'attributo [BaseType] .

Queste proprietà vengono sempre impostate insieme e devono avere lo stesso numero di elementi e devono essere mantenute sincronizzate. La Delegates matrice contiene una stringa per ogni delegato di tipo debole che si desidera eseguire il wrapping e la Events matrice contiene un tipo per ogni tipo a cui si desidera associarlo.

[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

Se si applica questo attributo quando vengono create nuove istanze di questa classe, l'istanza di tale oggetto verrà mantenuta intorno fino a quando non viene richiamato il metodo a cui fa riferimento .KeepRefUntil Ciò è utile per migliorare l'usabilità delle API, quando non si vuole che l'utente mantenga un riferimento a un oggetto per usare il codice. Il valore di questa proprietà è il nome di un metodo nella Delegate classe , pertanto è necessario usarlo anche in combinazione con le Events proprietà e Delegates .

L'esempio seguente illustra come viene usato da UIActionSheet in 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);
}

DesignatedDefaultCtorAttribute

Quando questo attributo viene applicato alla definizione dell'interfaccia, genererà un [DesignatedInitializer] attributo nel costruttore predefinito (generato), che esegue il mapping al init selettore.

DisableDefaultCtorAttribute

Quando questo attributo viene applicato alla definizione dell'interfaccia, impedirà al generatore di produrre il costruttore predefinito.

Usare questo attributo quando è necessario inizializzare l'oggetto con uno degli altri costruttori nella classe .

PrivateDefaultCtorAttribute

Quando questo attributo viene applicato alla definizione dell'interfaccia, contrassegnerà il costruttore predefinito come privato. Ciò significa che è comunque possibile creare un'istanza dell'oggetto di questa classe internamente dal file di estensione, ma non sarà accessibile solo agli utenti della classe.

Categoryattribute

Usare questo attributo in una definizione di tipo per associare Objective-C le categorie e per esporli come metodi di estensione C# per eseguire il mirroring del modo Objective-C in cui espone la funzionalità.

Le categorie sono un Objective-C meccanismo usato per estendere il set di metodi e proprietà disponibili in una classe. In pratica, vengono usati per estendere la funzionalità di una classe base (ad esempio ) quando un framework specifico è collegato in (ad esempio NSObjectUIKit), rendendo disponibili i relativi metodi, ma solo se il nuovo framework è collegato. In altri casi, vengono usati per organizzare le funzionalità in una classe in base alle funzionalità. Sono simili ai metodi di estensione C#.

Questa è l'aspetto di una categoria in Objective-C:

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

L'esempio precedente è disponibile in una libreria che estende le istanze di UIView con il metodo makeBackgroundRed.

Per associarli, è possibile usare l'attributo [Category] in una definizione di interfaccia. Quando si usa l'attributo [Category] , il significato dell'attributo passa dall'uso [BaseType] per specificare la classe di base da estendere, al tipo da estendere.

Di seguito viene illustrato come le UIView estensioni sono associate e trasformate in metodi di estensione C#:

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

Il codice precedente creerà una MyUIViewExtension classe che contiene il MakeBackgroundRed metodo di estensione. Ciò significa che è ora possibile chiamare MakeBackgroundRed su qualsiasi UIView sottoclasse, offrendo la stessa funzionalità che si otterrebbe su Objective-C.

In alcuni casi si troveranno membri statici all'interno di categorie come nell'esempio seguente:

@interface FooObject (MyFooObjectExtension)
+ (BOOL)boolMethod:(NSRange *)range;
@end

In questo modo si verifica una definizione di interfaccia C# di categoria non corretta :

[Category]
[BaseType (typeof (FooObject))]
interface FooObject_Extensions {

    // Incorrect Interface definition
    [Static]
    [Export ("boolMethod:")]
    bool BoolMethod (NSRange range);
}

Risposta errata perché per usare l'estensione necessaria un'istanza BoolMethod di FooObject ma si sta associando un'estensione statica ObjC, questo è un effetto collaterale dovuto al fatto che vengono implementati i metodi di estensione C#.

L'unico modo per usare le definizioni precedenti è il seguente codice brutto:

(null as FooObject).BoolMethod (range);

È consigliabile evitare questo problema per inline la BoolMethod definizione all'interno della definizione dell'interfaccia FooObject stessa, in modo da poter chiamare questa estensione come previsto FooObject.BoolMethod (range).

[BaseType (typeof (NSObject))]
interface FooObject {

    [Static]
    [Export ("boolMethod:")]
    bool BoolMethod (NSRange range);
}

Verrà visualizzato un avviso (BI1117) ogni volta che viene trovato un [Static] membro all'interno di una [Category] definizione. Se si desidera effettivamente avere [Static] membri all'interno [Category] delle definizioni, è possibile disattivare l'avviso usando [Category (allowStaticMembers: true)] o decorata la definizione del membro o [Category] dell'interfaccia con [Internal].

StaticAttribute

Quando questo attributo viene applicato a una classe, verrà generata solo una classe statica, una che non deriva da NSObject, quindi l'attributo [BaseType] viene ignorato. Le classi statiche vengono usate per ospitare variabili pubbliche C da esporre.

Ad esempio:

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

Genererà una classe C# con l'API seguente:

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

Definizioni di protocollo/modello

I modelli vengono in genere usati dall'implementazione del protocollo. Si differenziano dal fatto che il runtime verrà registrato solo con Objective-C i metodi effettivamente sovrascritti. In caso contrario, il metodo non verrà registrato.

Questo in generale significa che quando si sottoclassa una classe contrassegnata con , ModelAttributenon è consigliabile chiamare il metodo di base. La chiamata a tale metodo genererà l'eccezione seguente: Foundation.You_Should_Not_Call_base_In_This_Method. È consigliabile implementare l'intero comportamento nella sottoclasse per tutti i metodi di cui si esegue l'override.

AbstractAttribute

Per impostazione predefinita, i membri che fanno parte di un protocollo non sono obbligatori. In questo modo gli utenti possono creare una sottoclasse dell'oggetto Model semplicemente derivando dalla classe in C# ed eseguendo l'override solo dei metodi di cui si preoccupano. A volte il Objective-C contratto richiede che l'utente fornisca un'implementazione per questo metodo (quelli contrassegnati con la @required direttiva in Objective-C). In questi casi, è necessario contrassegnarli con l'attributo [Abstract] .

L'attributo [Abstract] può essere applicato a metodi o proprietà e fa in modo che il generatore contrassegni il membro generato come astratto e la classe sia una classe astratta.

Di seguito è riportato Xamarin.iOS:

[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UITableViewDataSource {
    [Export ("tableView:numberOfRowsInSection:")]
    [Abstract]
    nint RowsInSection (UITableView tableView, nint section);
}

DefaultValueAttribute

Specifica il valore predefinito da restituire da un metodo del modello se l'utente non fornisce un metodo per questo particolare metodo nell'oggetto Model

Sintassi:

public class DefaultValueAttribute : Attribute {
        public DefaultValueAttribute (object o);
        public object Default { get; set; }
}

Ad esempio, nella seguente classe del delegato immaginario per una Camera classe viene fornito un oggetto ShouldUploadToServer che verrebbe esposto come proprietà nella Camera classe . Se l'utente della Camera classe non imposta in modo esplicito un valore su un'espressione lambda che può rispondere a true o false, il valore predefinito restituito in questo caso sarà false, il valore specificato nell'attributo DefaultValue :

[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
    [Export ("camera:shouldPromptForAction:"), DefaultValue (false)]
    bool ShouldUploadToServer (Camera camera, CameraAction action);
}

Se l'utente imposta un gestore nella classe immaginaria, questo valore verrà ignorato:

var camera = new Camera ();
camera.ShouldUploadToServer = (camera, action) => return SomeDecision ();

Vedere anche: [NoDefaultValue], [DefaultValueFromArgument].

DefaultValueFromArgumentAttribute

Sintassi:

public class DefaultValueFromArgumentAttribute : Attribute {
    public DefaultValueFromArgumentAttribute (string argument);
    public string Argument { get; }
}

Questo attributo, se specificato in un metodo che restituisce un valore in una classe modello, indicherà al generatore di restituire il valore del parametro specificato se l'utente non ha fornito il proprio metodo o lambda.

Esempio:

[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
    [Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
    float ComputeAnimationCurve (NSAnimation animation, nfloat progress);
}

Nel caso precedente se l'utente della NSAnimation classe ha scelto di usare uno degli eventi/proprietà C# e non è stato impostato su NSAnimation.ComputeAnimationCurve un metodo o un'espressione lambda, il valore restituito sarà il valore passato nel parametro di stato.

Vedere anche: [NoDefaultValue], [DefaultValue]

IgnoredInDelegateAttribute

A volte non è opportuno esporre un evento o una proprietà delegato da una classe Model nella classe host, quindi l'aggiunta di questo attributo indicherà al generatore di evitare la generazione di qualsiasi metodo decorato con esso.

[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

Questo attributo viene usato nei metodi Model che restituiscono valori per impostare il nome della firma del delegato da utilizzare.

Esempio:

[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
    [Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
    float ComputeAnimationCurve (NSAnimation animation, float progress);
}

Con la definizione precedente, il generatore produrrà la dichiarazione pubblica seguente:

public delegate float NSAnimationProgress (MonoMac.AppKit.NSAnimation animation, float progress);

DelegateApiNameAttribute

Questo attributo viene usato per consentire al generatore di modificare il nome della proprietà generata nella classe host. A volte è utile quando il nome del metodo della classe FooDelegate ha senso per la classe Delegate, ma sembra strano nella classe host come proprietà.

Questo è molto utile (e necessario) quando si dispone di due o più metodi di overload che hanno senso mantenerli denominati così come si trova nella classe FooDelegate, ma si vuole esporli nella classe host con un nome migliore.

Esempio:

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

Con la definizione precedente, il generatore produrrà la seguente dichiarazione pubblica nella classe host:

public Func<NSAnimation, float, float> ComputeAnimationCurve { get; set; }

EventArgsAttribute

Per gli eventi che accettano più di un parametro (nella Objective-C convenzione è che il primo parametro in una classe delegate è l'istanza dell'oggetto mittente) è necessario specificare il nome che si desidera che la classe EventArgs generata sia. Questa operazione viene eseguita con l'attributo [EventArgs] nella dichiarazione del metodo nella Model classe .

Ad esempio:

[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
    [Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
    void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}

La dichiarazione precedente genererà una UIImagePickerImagePickedEventArgs classe che deriva da EventArgs e comprime entrambi i parametri, e UIImageNSDictionary. Il generatore produce questo:

public partial class UIImagePickerImagePickedEventArgs : EventArgs {
    public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
    public UIImage Image { get; set; }
    public NSDictionary EditingInfo { get; set; }
}

Espone quindi quanto segue nella UIImagePickerController classe :

public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }

EventNameAttribute

Questo attributo viene usato per consentire al generatore di modificare il nome di un evento o di una proprietà generata nella classe . A volte è utile quando il nome del metodo della classe Model ha senso per la classe del modello, ma sembra strano nella classe di origine come evento o proprietà.

Ad esempio, UIWebView usa il bit seguente da UIWebViewDelegate:

[Export ("webViewDidFinishLoad:"), EventArgs ("UIWebView"), EventName ("LoadFinished")]
void LoadingFinished (UIWebView webView);

Nell'esempio precedente viene esposto LoadingFinished come metodo in UIWebViewDelegate, ma LoadFinished come evento da associare a in un UIWebViewoggetto :

var webView = new UIWebView (...);
webView.LoadFinished += delegate { Console.WriteLine ("done!"); }

ModelAttribute

Quando si applica l'attributo [Model] a una definizione di tipo nell'API del contratto, il runtime genererà codice speciale che esernerà solo le chiamate ai metodi nella classe se l'utente ha sovrascritto un metodo nella classe . Questo attributo viene in genere applicato a tutte le API che eseplicano il wrapping di una Objective-C classe delegato.

Il runtime genererà anche una Objective-C classe che corrisponde al nome del protocollo corrispondente.

È possibile personalizzare il nome della Objective-C classe in due modi:

  1. Impostazione AutoGeneratedName = truedi :

    [Model (AutoGeneratedName = true)]
    

    In questo modo il runtime genererà un nome univoco per il Objective-C tipo. Il nome è attualmente basato sul nome dell'assembly e sul nome completo del tipo del modello (questo potrebbe cambiare in futuro).

  2. Specificare in modo esplicito il nome:

    [Model (Name = "CustomName")]
    

È consigliabile usare AutoGeneratedName = true. In .NET il nome viene sempre generato (a meno che non sia specificato in modo esplicito come in 2. sopra) e la AutoGeneratedName proprietà non esiste più.

NoDefaultValueAttribute

Specifica che il metodo nel modello non fornisce un valore restituito predefinito.

Questa operazione funziona con il Objective-C runtime rispondendo false alla Objective-C richiesta di runtime per determinare se il selettore specificato è implementato in questa classe.

[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
    [Export ("shouldDisplayPopup"), NoDefaultValue]
    bool ShouldUploadToServer ();
}

Vedere anche: [DefaultValue], [DefaultValueFromArgument]

Protocolli

Il Objective-C concetto di protocollo non esiste effettivamente in C#. I protocolli sono simili alle interfacce C#, ma differiscono in quanto non tutti i metodi e le proprietà dichiarati in un protocollo devono essere implementati dalla classe che lo adotta. Invece alcuni dei metodi e delle proprietà sono facoltativi.

Alcuni protocolli vengono in genere usati come classi Model, che devono essere associati usando l'attributo [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 ();
}

A partire da Xamarin.iOS 7.0 è stata incorporata una funzionalità di associazione di protocollo nuova e migliorata. Qualsiasi definizione che contiene l'attributo [Protocol] genererà effettivamente tre classi di supporto che migliorano notevolmente il modo in cui si utilizzano i protocolli:

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

L'implementazione della classe fornisce una classe astratta completa che è possibile eseguire l'override dei singoli metodi di e ottenere la sicurezza completa dei tipi. Tuttavia, a causa di C# non supporta l'ereditarietà multipla, esistono scenari in cui potrebbe essere necessaria una classe di base diversa, ma si vuole comunque implementare un'interfaccia.

È qui che entra in gioco la definizione dell'interfaccia generata. Si tratta di un'interfaccia che dispone di tutti i metodi necessari dal protocollo. Ciò consente agli sviluppatori che vogliono implementare il protocollo per implementare semplicemente l'interfaccia . Il runtime registrerà automaticamente il tipo come adozione del protocollo.

Si noti che l'interfaccia elenca solo i metodi richiesti ed espone i metodi facoltativi. Ciò significa che le classi che adottano il protocollo otterranno il controllo completo dei tipi per i metodi richiesti, ma dovranno ricorrere alla digitazione debole (usando manualmente gli attributi Export e la corrispondenza della firma) per i metodi di protocollo facoltativi.

Per semplificare l'utilizzo di un'API che usa protocolli, lo strumento di associazione produrrà anche una classe di metodi di estensione che espone tutti i metodi facoltativi. Ciò significa che, purché si stia usando un'API, sarà possibile considerare i protocolli come tutti i metodi.

Se si vogliono usare le definizioni di protocollo nell'API, è necessario scrivere le interfacce vuote nella definizione dell'API. Se si vuole usare MyProtocol in un'API, è necessario eseguire questa operazione:

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

Il codice precedente è necessario perché in fase di associazione l'oggetto IMyProtocol non esiste, ecco perché è necessario fornire un'interfaccia vuota.

Adozione di interfacce generate dal protocollo

Ogni volta che si implementa una delle interfacce generate per i protocolli, come illustrato di seguito:

class MyDelegate : NSObject, IUITableViewDelegate {
    nint IUITableViewDelegate.GetRowHeight (nint row) {
        return 1;
    }
}

L'implementazione per i metodi di interfaccia necessari viene esportata con il nome corretto, pertanto equivale a questo:

class MyDelegate : NSObject, IUITableViewDelegate {
    [Export ("getRowHeight:")]
    nint IUITableViewDelegate.GetRowHeight (nint row) {
        return 1;
    }
}

Questo funzionerà per tutti i membri del protocollo necessari, ma esiste un caso speciale con selettori facoltativi di cui tenere conto.

I membri del protocollo facoltativi vengono trattati in modo identico quando si usa la classe base:

public class UrlSessionDelegate : NSUrlSessionDownloadDelegate {
	public override void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)

ma quando si usa l'interfaccia del protocollo è necessario aggiungere [Export]. L'IDE lo aggiungerà tramite completamento automatico quando lo si aggiunge a partire dall'override.

public class UrlSessionDelegate : NSObject, INSUrlSessionDownloadDelegate {
	[Export ("URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:")]
	public void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)

Esiste una leggera differenza di comportamento tra i due in fase di esecuzione:

  • Gli utenti della classe base (NSUrlSessionDownloadDelegate, ad esempio) forniscono tutti i selettori obbligatori e facoltativi, restituendo valori predefiniti ragionevoli.
  • Gli utenti dell'interfaccia (INSUrlSessionDownloadDelegate in esempio) rispondono solo ai selettori esatti forniti.

Alcune classi rare possono comportarsi in modo diverso qui. In quasi tutti i casi, tuttavia, è sicuro usarlo.

Inlining del protocollo

Anche se si associano tipi esistenti Objective-C dichiarati come adozione di un protocollo, è consigliabile inline direttamente il protocollo. A tale scopo, dichiarare semplicemente il protocollo come interfaccia senza [BaseType] alcun attributo ed elencare il protocollo nell'elenco delle interfacce di base per l'interfaccia.

Esempio:

interface SpeakProtocol {
    [Export ("say:")]
    void Say (string msg);
}

[BaseType (typeof (NSObject))]
interface Robot : SpeakProtocol {
    [Export ("awake")]
    bool Awake { get; set; }
}

Definizioni dei membri

Gli attributi in questa sezione vengono applicati ai singoli membri di un tipo: proprietà e dichiarazioni di metodo.

AlignAttribute

Utilizzato per specificare il valore di allineamento per i tipi restituiti della proprietà. Alcune proprietà accettano puntatori agli indirizzi che devono essere allineati a determinati limiti (in Xamarin.iOS, ad esempio con alcune GLKBaseEffect proprietà che devono essere allineate a 16 byte). È possibile utilizzare questa proprietà per decorare il getter e usare il valore di allineamento. Questo viene in genere usato con i OpenTK.Vector4 tipi e OpenTK.Matrix4 quando si integra con Objective-C le API.

Esempio:

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

AppearanceAttribute

L'attributo [Appearance] è limitato a iOS 5, in cui è stato introdotto gestione aspetto.

L'attributo [Appearance] può essere applicato a qualsiasi metodo o proprietà che partecipa al UIAppearance framework. Quando questo attributo viene applicato a un metodo o a una proprietà in una classe, indirizzerà il generatore di associazioni a creare una classe di aspetto fortemente tipizzata usata per applicare uno stile a tutte le istanze di questa classe o alle istanze che soddisfano determinati criteri.

Esempio:

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

L'esempio precedente genera il codice seguente in 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)

Utilizzare su [AutoReleaseAttribute] metodi e proprietà per eseguire il wrapping della chiamata al metodo in un oggetto NSAutoReleasePool.

In Objective-C sono disponibili alcuni metodi che restituiscono valori aggiunti all'oggetto predefinito NSAutoReleasePool. Per impostazione predefinita, questi passano al thread NSAutoReleasePool, ma poiché Xamarin.iOS mantiene anche un riferimento agli oggetti purché l'oggetto gestito si trovi, potrebbe non essere necessario mantenere un riferimento aggiuntivo in , NSAutoReleasePool che verrà svuotato solo fino a quando il thread non restituisce il controllo al thread successivo oppure si torna al ciclo principale.

Questo attributo viene applicato, ad esempio, alle proprietà pesanti ( ad esempio UIImage.FromFile) che restituisce gli oggetti aggiunti all'oggetto predefinito NSAutoReleasePool. Senza questo attributo, le immagini vengono mantenute finché il thread non ha restituito il controllo al ciclo principale. Uf il thread era una sorta di downloader di sfondo che è sempre vivo e in attesa di lavoro, le immagini non verrebbero mai rilasciate.

ForcedTypeAttribute

Viene [ForcedTypeAttribute] utilizzato per applicare la creazione di un tipo gestito anche se l'oggetto non gestito restituito non corrisponde al tipo descritto nella definizione dell'associazione.

Ciò è utile quando il tipo descritto in un'intestazione non corrisponde al tipo restituito del metodo nativo, ad esempio accettare la definizione seguente Objective-C da NSURLSession:

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

Indica chiaramente che restituirà un'istanzaNSURLSessionDownloadTask, ma restituisce tuttavia un NSURLSessionTaskoggetto , che è una superclasse e quindi non convertibile in NSURLSessionDownloadTask. Poiché ci si trova in un contesto indipendente dai tipi, si verificherà un'operazione InvalidCastException .

Per rispettare la descrizione dell'intestazione ed evitare , InvalidCastExceptionviene usato .[ForcedTypeAttribute]

[BaseType (typeof (NSObject), Name="NSURLSession")]
interface NSUrlSession {

    [Export ("downloadTaskWithRequest:")]
    [return: ForcedType]
    NSUrlSessionDownloadTask CreateDownloadTask (NSUrlRequest request);
}

Accetta [ForcedTypeAttribute] anche un valore booleano denominato Owns che è false per impostazione predefinita [ForcedType (owns: true)]. Il parametro proprietario viene usato per seguire i criteri di proprietà per gli oggetti Core Foundation .

è [ForcedTypeAttribute] valido solo per parametri, proprietà e valore restituito.

BindAsAttribute

consente l'associazione [BindAsAttribute]NSNumbere NSValue (enumerazioni NSString) in tipi C# più accurati. L'attributo può essere usato per creare un'API .NET più accurata e più accurata tramite l'API nativa.

È possibile decorare i metodi (sul valore restituito), i parametri e le proprietà con BindAs. L'unica restrizione è che il membro non deve trovarsi all'interno di un'interfaccia [Protocol] o [Model] .

Ad esempio:

[return: BindAs (typeof (bool?))]
[Export ("shouldDrawAt:")]
NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect);

Restituirebbe:

[Export ("shouldDrawAt:")]
bool? ShouldDraw (CGRect rect) { ... }

Internamente faremo le bool?conversioni ->NSNumber e CGRect<->NSValue .<

I tipi di incapsulamento supportati correnti sono:

  • NSValue
  • NSNumber
  • NSString

NSValue

I tipi di dati C# seguenti sono supportati per essere incapsulati da/in NSValue:

  • CGAffineTransform
  • NSRange
  • CGVector
  • SCNMatrix4
  • CLLocationCoordinate2D
  • SCNVector3
  • SCNVector4
  • CGPoint/PointF
  • CGRect/RectangleF
  • CGSize/SizeF
  • UIEdgeInsets
  • UIOffset
  • MKCoordinateSpan
  • CMTimeRange
  • CMTime
  • CMTimeMapping
  • CATransform3D

NSNumber

I tipi di dati C# seguenti sono supportati per essere incapsulati da/in NSNumber:

  • bool
  • byte
  • double
  • float
  • short
  • int
  • long
  • sbyte
  • ushort
  • uint
  • ulong
  • nfloat
  • nint
  • nuint
  • Enumerazioni

NSString

[BindAs] funziona in combinazione con le enumerazioni supportate da una costante NSString, in modo da poter creare un'API .NET migliore, ad esempio:

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

Restituirebbe:

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

Verrà gestita la enum<conversione -NSString> solo se il tipo di enumerazione fornito in [BindAs] è supportato da una costante NSString.

Matrici

[BindAs] supporta anche matrici di uno dei tipi supportati, è possibile avere la definizione API seguente come esempio:

[return: BindAs (typeof (CAScroll []))]
[Export ("getScrollModesAt:")]
NSString [] GetScrollModes ([BindAs (typeof (CGRect []))] NSValue [] rects);

Restituirebbe:

[Export ("getScrollModesAt:")]
CAScroll? [] GetScrollModes (CGRect [] rects) { ... }

Il rects parametro verrà incapsulato in un NSArray oggetto che contiene un oggetto NSValue per ognuno CGRect e in cambio si otterrà una matrice di CAScroll? cui è stata creata usando i valori dell'oggetto restituito NSArray contenente NSStrings.

BindAttribute

L'attributo [Bind] ha due utilizzi uno quando viene applicato a un metodo o a una dichiarazione di proprietà e un altro quando viene applicato al singolo getter o setter in una proprietà.

Se utilizzato per un metodo o una proprietà, l'effetto dell'attributo consiste nel [Bind] generare un metodo che richiama il selettore specificato. Ma il metodo generato risultante non è decorato con l'attributo , il che significa che non può partecipare all'override [Export] del metodo. Viene in genere usato in combinazione con l'attributo per l'implementazione [Target]Objective-C di metodi di estensione.

Ad esempio:

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

Se usato in un getter o setter, l'attributo [Bind] viene usato per modificare le impostazioni predefinite dedotte dal generatore di codice durante la generazione dei nomi del selettore getter e setter Objective-C per una proprietà. Per impostazione predefinita, quando si contrassegna una proprietà con il nome fooBar, il generatore genera un'esportazione fooBar per il getter e setFooBar: per il setter. In alcuni casi, Objective-C non segue questa convenzione, in genere modificano il nome del getter in modo che sia isFooBar. Questo attributo verrà usato per informare il generatore di questo attributo.

Ad esempio:

// Default behavior
[Export ("active")]
bool Active { get; set; }

// Custom naming with the Bind attribute
[Export ("visible")]
bool Visible { [Bind ("isVisible")] get; set; }

AsyncAttribute

Disponibile solo in Xamarin.iOS 6.3 e versioni successive.

Questo attributo può essere applicato ai metodi che accettano un gestore di completamento come ultimo argomento.

È possibile utilizzare l'attributo [Async] nei metodi il cui ultimo argomento è un callback. Quando si applica a un metodo, il generatore di associazioni genererà una versione di tale metodo con il suffisso Async. Se il callback non accetta parametri, il valore restituito sarà , Taskse il callback accetta un parametro, il risultato sarà .Task<T>

[Export ("upload:complete:")]
[Async]
void LoadFile (string file, NSAction complete)

Di seguito verrà generato questo metodo asincrono:

Task LoadFileAsync (string file);

Se il callback accetta più parametri, è necessario impostare ResultType o ResultTypeName per specificare il nome desiderato del tipo generato che conterrà tutte le proprietà.

delegate void OnComplete (string [] files, nint byteCount);

[Export ("upload:complete:")]
[Async (ResultTypeName="FileLoading")]
void LoadFiles (string file, OnComplete complete)

Di seguito verrà generato questo metodo asincrono, dove FileLoading contiene le proprietà per accedere files sia a che byteCounta :

Task<FileLoading> LoadFile (string file);

Se l'ultimo parametro del callback è , NSErroril metodo generato Async verificherà se il valore non è Null e, in tal caso, il metodo asincrono generato imposta l'eccezione dell'attività.

[Export ("upload:onComplete:")]
[Async]
void Upload (string file, Action<string,NSError> onComplete);

Il codice precedente genera il metodo asincrono seguente:

Task<string> UploadAsync (string file);

E in caso di errore, l'attività risultante avrà l'eccezione impostata su un NSErrorException oggetto che esegue il wrapping dell'oggetto risultante NSError.

AsyncAttribute.ResultType

Utilizzare questa proprietà per specificare il valore per l'oggetto restituito Task . Questo parametro accetta un tipo esistente, quindi deve essere definito in una delle definizioni api di base.

AsyncAttribute.ResultTypeName

Utilizzare questa proprietà per specificare il valore per l'oggetto restituito Task . Questo parametro accetta il nome del tipo desiderato, il generatore produrrà una serie di proprietà, una per ogni parametro che il callback accetta.

AsyncAttribute.MethodName

Utilizzare questa proprietà per personalizzare il nome dei metodi asincroni generati. L'impostazione predefinita consiste nell'usare il nome del metodo e aggiungere il testo "Async", è possibile usarlo per modificare questo valore predefinito.

DesignatedInitializerAttribute

Quando questo attributo viene applicato a un costruttore, verrà generato lo stesso [DesignatedInitializer] nell'assembly della piattaforma finale. Ciò consente all'IDE di indicare quale costruttore deve essere usato nelle sottoclassi.

Deve essere eseguito il mapping all'uso Objective-Cdi /clang di __attribute__((objc_designated_initializer)).

DisableZeroCopyAttribute

Questo attributo viene applicato ai parametri stringa o alle proprietà stringa e indica al generatore di codice di non usare il marshalling della stringa di copia zero per questo parametro e di creare invece una nuova istanza NSString dalla stringa C#. Questo attributo è obbligatorio solo per le stringhe se si indica al generatore di usare il marshalling di stringhe di copia zero usando l'opzione della --zero-copy riga di comando o impostando l'attributo ZeroCopyStringsAttributea livello di assembly .

Ciò è necessario nei casi in cui la proprietà viene dichiarata come Objective-C una retain proprietà o assign anziché una copy proprietà . Questi si verificano in genere nelle librerie di terze parti che sono state erroneamente "ottimizzate" dagli sviluppatori. In generale, retain o assignNSString le proprietà non sono corrette perché NSMutableString o le classi derivate dall'utente di NSString potrebbero modificare il contenuto delle stringhe senza conoscere il codice della libreria, interrompendo in modo secondario l'applicazione. In genere ciò si verifica a causa di un'ottimizzazione prematura.

Di seguito sono illustrate due proprietà di questo tipo in Objective-C:

@property(nonatomic,retain) NSString *name;
@property(nonatomic,assign) NSString *name2;

DisposeAttribute

Quando si applica a [DisposeAttribute] una classe, si fornisce un frammento di codice che verrà aggiunto all'implementazione Dispose() del metodo della classe .

Poiché il Dispose metodo viene generato automaticamente dallo bgen strumento, è necessario usare l'attributo [Dispose] per inserire codice nell'implementazione del metodo generato Dispose .

Ad esempio:

[BaseType (typeof (NSObject))]
[Dispose ("if (OpenConnections > 0) CloseAllConnections ();")]
interface DatabaseConnection {
}

ExportAttribute

L'attributo [Export] viene usato per contrassegnare un metodo o una proprietà da esporre al Objective-C runtime. Questo attributo viene condiviso tra lo strumento di associazione e i runtime Xamarin.iOS e Xamarin.Mac effettivi. Per i metodi, il parametro viene passato verbatim al codice generato, per le proprietà viene generato un getter e un setter Export in base alla dichiarazione di base (vedere la sezione relativa [BindAttribute] a per informazioni su come modificare il comportamento dello strumento di associazione).

Sintassi:

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

Il selettore rappresenta il nome del metodo o della proprietà sottostante Objective-C associato.

ExportAttribute.ArgumentSemantic

FieldAttribute

Questo attributo viene usato per esporre una variabile globale C come campo caricato su richiesta ed esposto al codice C#. In genere è necessario per ottenere i valori delle costanti definite in C o Objective-C che potrebbero essere token usati in alcune API o i cui valori sono opachi e devono essere usati così come sono dal codice utente.

Sintassi:

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 è il simbolo C con cui collegarsi. Per impostazione predefinita, questo verrà caricato da una libreria il cui nome viene dedotto dallo spazio dei nomi in cui è definito il tipo. Se non si tratta della libreria in cui viene cercato il simbolo, è necessario passare il libraryName parametro . Se si collega una libreria statica, usare __Internal come libraryName parametro .

Le proprietà generate sono sempre statiche.

Le proprietà contrassegnate con l'attributo Field possono essere dei tipi seguenti:

  • NSString
  • NSArray
  • nint / int / long
  • nuint / uint / ulong
  • nfloat / float
  • double
  • CGSize
  • System.IntPtr
  • Enumerazioni

I setter non sono supportati per le enumerazioni supportate dalle costanti NSString, ma possono essere associate manualmente, se necessario.

Esempio:

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

InternalAttribute

L'attributo [Internal] può essere applicato a metodi o proprietà e ha l'effetto di contrassegnare il codice generato con la internal parola chiave C# rendendo il codice accessibile solo al codice nell'assembly generato. Questo viene in genere usato per nascondere le API troppo basse o fornire un'API pubblica non ottimale su cui si vuole migliorare o per le API che non sono supportate dal generatore e richiedono codice manuale.

Quando si progetta l'associazione, in genere si nasconde il metodo o la proprietà usando questo attributo e si specifica un nome diverso per il metodo o la proprietà e quindi nel file di supporto complementare C# si aggiunge un wrapper fortemente tipizzato che espone la funzionalità sottostante.

Ad esempio:

[Internal]
[Export ("setValue:forKey:")]
void _SetValueForKey (NSObject value, NSObject key);

[Internal]
[Export ("getValueForKey:")]
NSObject _GetValueForKey (NSObject key);

Quindi, nel file di supporto è possibile avere codice simile al seguente:

public NSObject this [NSObject idx] {
    get {
        return _GetValueForKey (idx);
    }
    set {
        _SetValueForKey (value, idx);
    }
}

IsThreadStaticAttribute

Questo attributo contrassegna il campo sottostante per una proprietà da annotare con l'attributo .NET [ThreadStatic] . Ciò è utile se il campo è una variabile statica del thread.

MarshalNativeExceptions (Xamarin.iOS 6.0.6)

Questo attributo eseguirà un metodo che supporta le eccezioni native (Objective-C). Invece di chiamare objc_msgSend direttamente, la chiamata passerà attraverso un trampolino personalizzato che intercetta le eccezioni ObjectiveC e le effettua il marshalling in eccezioni gestite.

Attualmente sono supportate solo alcune objc_msgSend firme (si scoprirà se una firma non è supportata quando il collegamento nativo di un'app che usa l'associazione ha esito negativo con un xamarin_ mancante_objc_msgSend simbolo), ma è possibile aggiungere altro alla richiesta.

NewAttribute

Questo attributo viene applicato ai metodi e alle proprietà per fare in modo che il generatore generi la new parola chiave davanti alla dichiarazione.

Viene usato per evitare avvisi del compilatore quando lo stesso metodo o lo stesso nome di proprietà viene introdotto in una sottoclasse già esistente in una classe di base.

NotificationAttribute

È possibile applicare questo attributo ai campi per fare in modo che il generatore producano una classe di notifiche helper fortemente tipizzata.

Questo attributo può essere usato senza argomenti per le notifiche che non contengono payload oppure è possibile specificare un System.Type oggetto che fa riferimento a un'altra interfaccia nella definizione dell'API, in genere con il nome che termina con "EventArgs". Il generatore trasformerà l'interfaccia in una classe che sottoclassi EventArgs e includerà tutte le proprietà elencate. L'attributo [Export] deve essere usato nella EventArgs classe per elencare il nome della chiave usata per cercare il Objective-C dizionario per recuperare il valore.

Ad esempio:

interface MyClass {
    [Notification]
    [Field ("MyClassDidStartNotification")]
    NSString DidStartNotification { get; }
}

Il codice precedente genererà una classe MyClass.Notifications annidata con i metodi seguenti:

public class MyClass {
   [..]
   public Notifications {
      public static NSObject ObserveDidStart (EventHandler<NSNotificationEventArgs> handler)
      public static NSObject ObserveDidStart (NSObject objectToObserve, EventHandler<NSNotificationEventArgs> handler)
   }
}

Gli utenti del codice possono quindi sottoscrivere facilmente le notifiche inviate a NSDefaultCenter usando codice simile al seguente:

var token = MyClass.Notifications.ObserverDidStart ((notification) => {
    Console.WriteLine ("Observed the 'DidStart' event!");
});

Oppure per impostare un oggetto specifico da osservare. Se si passa null a objectToObserve questo metodo si comporterà esattamente come il suo altro peer.

var token = MyClass.Notifications.ObserverDidStart (objectToObserve, (notification) => {
    Console.WriteLine ("Observed the 'DidStart' event on objectToObserve!");
});

Il valore restituito da ObserveDidStart può essere usato per interrompere facilmente la ricezione di notifiche, come illustrato di seguito:

token.Dispose ();

In alternativa, è possibile chiamare NSNotification.DefaultCenter.RemoveObserver e passare il token. Se la notifica contiene parametri, è necessario specificare un'interfaccia helper EventArgs , come illustrato di seguito:

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

La classe precedente genererà una MyScreenChangedEventArgs classe con le ScreenX proprietà e ScreenY che recupererà i dati dal dizionario NSNotification.UserInfo usando rispettivamente le chiavi ScreenXKey e ScreenYKey applicherà le conversioni appropriate. L'attributo [ProbePresence] viene usato per il generatore per eseguire il probe se la chiave è impostata in UserInfo, anziché tentare di estrarre il valore. Viene usato per i casi in cui la presenza della chiave è il valore (in genere per i valori booleani).

In questo modo è possibile scrivere codice simile al seguente:

var token = MyClass.NotificationsObserveScreenChanged ((notification) => {
    Console.WriteLine ("The new screen dimensions are {0},{1}", notification.ScreenX, notification.ScreenY);
});

In alcuni casi, non esiste alcuna costante associata al valore passato nel dizionario. Apple a volte usa costanti di simboli pubblici e talvolta usa costanti stringa. Per impostazione predefinita, l'attributo [Export] nella classe specificata EventArgs userà il nome specificato come simbolo pubblico per essere cercato in fase di esecuzione. Se questo non è il caso, e invece dovrebbe essere cercato come costante stringa, quindi passare il ArgumentSemantic.Assign valore all'attributo Export.

Novità di Xamarin.iOS 8.4

A volte, le notifiche inizieranno la vita senza argomenti, quindi l'uso di [Notification] senza argomenti è accettabile. In alcuni casi, tuttavia, verranno introdotti i parametri per la notifica. Per supportare questo scenario, l'attributo può essere applicato più volte.

Se si sviluppa un'associazione e si vuole evitare l'interruzione del codice utente esistente, si trasformerebbe una notifica esistente da:

interface MyClass {
    [Notification]
    [Field ("MyClassScreenChangedNotification")]
    NSString ScreenChangedNotification { get; }
}

In una versione che elenca due volte l'attributo di notifica, come illustrato di seguito:

interface MyClass {
    [Notification]
    [Notification (typeof (MyScreenChangedEventArgs)]
    [Field ("MyClassScreenChangedNotification")]
    NSString ScreenChangedNotification { get; }
}

NullAllowedAttribute

Quando questa proprietà viene applicata a una proprietà, contrassegna la proprietà in modo che consenta l'assegnazione del valore null . Questa opzione è valida solo per i tipi di riferimento.

Quando viene applicato a un parametro in una firma del metodo, indica che il parametro specificato può essere Null e che non deve essere eseguito alcun controllo per il passaggio di null valori.

Se il tipo di riferimento non dispone di questo attributo, lo strumento di associazione genererà un controllo del valore assegnato prima di passarlo a Objective-C e genererà un controllo che genererà un'eccezione ArgumentNullException se il valore assegnato è null.

Ad esempio:

// In properties

[NullAllowed]
UIImage IconFile { get; set; }

// In methods
void SetImage ([NullAllowed] UIImage image, State forState);

OverrideAttribute

Usare questo attributo per indicare al generatore di associazioni che l'associazione per questo particolare metodo deve essere contrassegnata con una override parola chiave.

PreSnippetAttribute

È possibile usare questo attributo per inserire codice da inserire dopo la convalida dei parametri di input, ma prima che il codice chiami in Objective-C.

Esempio:

[Export ("demo")]
[PreSnippet ("var old = ViewController;")]
void Demo ();

PrologueSnippetAttribute

È possibile usare questo attributo per inserire codice da inserire prima che uno dei parametri venga convalidato nel metodo generato.

Esempio:

[Export ("demo")]
[Prologue ("Trace.Entry ();")]
void Demo ();

PostGetAttribute

Indica al generatore di associazioni di richiamare la proprietà specificata da questa classe per recuperare un valore da esso.

Questa proprietà viene in genere utilizzata per aggiornare la cache che punta a oggetti di riferimento che mantengono a cui fa riferimento l'oggetto grafico. In genere viene visualizzato nel codice con operazioni come Aggiungi/Rimuovi. Questo metodo viene usato in modo che dopo l'aggiunta o la rimozione degli elementi che la cache interna venga aggiornata per garantire che vengano conservati riferimenti gestiti agli oggetti effettivamente in uso. Ciò è possibile perché lo strumento di associazione genera un campo sottostante per tutti gli oggetti di riferimento in una determinata associazione.

Esempio:

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

In questo caso, la Dependencies proprietà verrà richiamata dopo l'aggiunta o la rimozione di dipendenze dall'oggetto NSOperation , assicurandosi di disporre di un grafico che rappresenta gli oggetti caricati effettivi, impedendo sia perdite di memoria che danneggiamento della memoria.

PostSnippetAttribute

È possibile usare questo attributo per inserire codice sorgente C# da inserire dopo che il codice ha richiamato il metodo sottostante Objective-C

Esempio:

[Export ("demo")]
[PostSnippet ("if (old != null) old.DemoComplete ();")]
void Demo ();

ProxyAttribute

Questo attributo viene applicato per restituire valori per contrassegnarli come oggetti proxy. Alcune Objective-C API restituiscono oggetti proxy che non possono essere differenziati dalle associazioni utente. L'effetto di questo attributo consiste nel contrassegnare l'oggetto come DirectBinding oggetto . Per uno scenario in Xamarin.Mac, è possibile visualizzare la discussione su questo bug.

ReleaseAttribute (Xamarin.iOS 6.0)

Questa operazione può essere applicata ai tipi restituiti per indicare che il generatore deve chiamare Release sull'oggetto prima di restituirlo. Questa operazione è necessaria solo quando un metodo fornisce un oggetto conservato (anziché un oggetto rilasciato automaticamente, che è lo scenario più comune)

Esempio:

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

Inoltre, questo attributo viene propagato al codice generato, in modo che il runtime di Xamarin.iOS sappia che deve conservare l'oggetto al rientro Objective-C da tale funzione.

SealedAttribute

Indica al generatore di contrassegnare il metodo generato come sealed. Se questo attributo non viene specificato, l'impostazione predefinita consiste nel generare un metodo virtuale (un metodo virtuale, un metodo astratto o un override a seconda della modalità di utilizzo di altri attributi).

StaticAttribute

Quando l'attributo [Static] viene applicato a un metodo o a una proprietà, viene generato un metodo o una proprietà statica. Se questo attributo non viene specificato, il generatore produce un metodo o una proprietà di istanza.

TransientAttribute

Utilizzare questo attributo per contrassegnare le proprietà i cui valori sono temporanei, ovvero oggetti creati temporaneamente da iOS, ma non di lunga durata. Quando questo attributo viene applicato a una proprietà, il generatore non crea un campo sottostante per questa proprietà, il che significa che la classe gestita non mantiene un riferimento all'oggetto.

WrapAttribute

Nella progettazione dei binding Xamarin.iOS/Xamarin.Mac, l'attributo viene usato per eseguire il [Wrap] wrapping di un oggetto con tipizzato debole con un oggetto fortemente tipizzato. Ciò entra in gioco principalmente con Objective-C gli oggetti delegati che vengono in genere dichiarati come di tipo id o NSObject. La convenzione usata da Xamarin.iOS e Xamarin.Mac consiste nell'esporre tali delegati o origini dati come di tipo NSObject e vengono denominati usando la convenzione "Weak" + il nome esposto. Una id delegate proprietà di Objective-C verrebbe esposta come NSObject WeakDelegate { get; set; } proprietà nel file del contratto API.

Ma in genere il valore assegnato a questo delegato è di un tipo sicuro, quindi si espongono il tipo sicuro e si applica l'attributo [Wrap] , questo significa che gli utenti possono scegliere di usare tipi deboli se hanno bisogno di un controllo fine o se devono ricorrere a trucchi di basso livello o possono usare la proprietà fortemente tipizzata per la maggior parte del lavoro.

Esempio:

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

Questo è il modo in cui l'utente userà la versione tipizzata debole del delegato:

// The weak case, user has to roll his own
class SomeObject : NSObject {
    [Export ("doDemo")]
    void CallbackForDoDemo () {}

}

var demo = new Demo ();
demo.WeakDelegate = new SomeObject ();

E questo è il modo in cui l'utente userebbe la versione fortemente tipizzata, si noti che l'utente sfrutta il sistema di tipi di C#e usa la parola chiave override per dichiararne la finalità e non deve decorare manualmente il metodo con [Export], poiché è stato eseguito il funzionamento nell'associazione per l'utente:

// This is the strong case,
class MyDelegate : DemoDelegate {
   override void Demo DoDemo () {}
}

var strongDemo = new Demo ();
demo.Delegate = new MyDelegate ();

Un altro uso dell'attributo consiste nel [Wrap] supportare una versione fortemente tipizzata dei metodi. Ad esempio:

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

Quando l'attributo [Wrap] viene applicato a un metodo all'interno di un tipo decorato con un [Category] attributo, è necessario includere This come primo argomento perché viene generato un metodo di estensione. Ad esempio:

[Wrap ("Write (This, image, options?.Dictionary, out error)")]
bool Write (CIImage image, CIImageRepresentationOptions options, out NSError error);

I membri generati da [Wrap] non sono per impostazione predefinita, se è necessario un virtual membro che è possibile impostare true sul parametro facoltativo isVirtualvirtual.

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

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

[Wrap] può anche essere usato direttamente nei getter e nei setter delle proprietà. In questo modo è possibile avere il controllo completo su di essi e regolare il codice in base alle esigenze. Si consideri ad esempio la definizione api seguente che usa enumerazioni intelligenti:

// Smart enum.
enum PersonRelationship {
        [Field (null)]
        None,

        [Field ("FMFather", "__Internal")]
        Father,

        [Field ("FMMother", "__Internal")]
        Mother
}

Definizione dell'interfaccia:

// Property definition.

[Export ("presenceType")]
NSString _PresenceType { get; set; }

PersonRelationship PresenceType {
    [Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType)")]
    get;
    [Wrap ("_PresenceType = value.GetConstant ()")]
    set;
}

Attributi di parametro

In questa sezione vengono descritti gli attributi che è possibile applicare ai parametri in una definizione di metodo, nonché a [NullAttribute] che si applica a una proprietà nel suo complesso.

BlockCallback

Questo attributo viene applicato ai tipi di parametro nelle dichiarazioni di delegato C# per notificare al gestore di associazione che il parametro in questione è conforme alla convenzione di Objective-C chiamata di blocco e deve eseguirne il marshalling in questo modo.

Viene in genere usato per i callback definiti come questo in Objective-C:

typedef returnType (^SomeTypeDefinition) (int parameter1, NSString *parameter2);

Vedere anche: CCallback.

CCallback

Questo attributo viene applicato ai tipi di parametro nelle dichiarazioni di delegato C# per notificare al binder che il parametro in questione è conforme alla convenzione di chiamata del puntatore a funzione ABI C e deve eseguirne il marshalling in questo modo.

Viene in genere usato per i callback definiti come questo in Objective-C:

typedef returnType (*SomeTypeDefinition) (int parameter1, NSString *parameter2);

Vedere anche: BlockCallback.

Params (Parametri)

È possibile usare l'attributo sull'ultimo [Params] parametro di matrice di una definizione di metodo per fare in modo che il generatore inserisca un "params" nella definizione. Ciò consente all'associazione di consentire facilmente parametri facoltativi.

Ad esempio, la definizione seguente:

[Export ("loadFiles:")]
void LoadFiles ([Params]NSUrl [] files);

Consente di scrivere il codice seguente:

foo.LoadFiles (new NSUrl (url));
foo.LoadFiles (new NSUrl (url1), new NSUrl (url2), new NSUrl (url3));

Questo ha il vantaggio aggiunto che non richiede agli utenti di creare una matrice esclusivamente per il passaggio di elementi.

PlainString

È possibile usare l'attributo [PlainString] davanti ai parametri stringa per indicare al generatore di associazioni di passare la stringa come stringa C, anziché passare il parametro come .NSString

La maggior parte delle Objective-C API usa NSString parametri, ma alcune API espongono un'API char * per passare stringhe, anziché la NSString variante. Usare [PlainString] in questi casi.

Ad esempio, le dichiarazioni seguenti Objective-C :

- (void) setText: (NSString *) theText;
- (void) logMessage: (char *) message;

Deve essere associato come segue:

[Export ("setText:")]
void SetText (string theText);

[Export ("logMessage:")]
void LogMessage ([PlainString] string theText);

RetainAttribute

Indica al generatore di mantenere un riferimento al parametro specificato. Il generatore fornirà l'archivio di backup per questo campo oppure è possibile specificare un nome (il WrapName) in cui archiviare il valore. Ciò è utile per contenere un riferimento a un oggetto gestito passato come parametro a Objective-C e quando si sa che Objective-C manterrà solo questa copia dell'oggetto. Ad esempio, un'API come SetDisplay (SomeObject) userebbe questo attributo perché è probabile che SetDisplay possa visualizzare un solo oggetto alla volta. Se è necessario tenere traccia di più oggetti , ad esempio per un'API di tipo Stack, usare l'attributo [RetainList] .

Sintassi:

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

TransientAttribute

Questo attributo viene applicato ai parametri e viene usato solo durante la transizione da Objective-C a C#. Durante queste transizioni i vari Objective-CNSObject parametri vengono inseriti in una rappresentazione gestita dell'oggetto.

Il runtime accetta un riferimento all'oggetto nativo e mantiene il riferimento fino a quando l'ultimo riferimento gestito all'oggetto non è più disponibile e il GC ha la possibilità di eseguire.

In alcuni casi, è importante che il runtime C# non mantenga un riferimento all'oggetto nativo. Ciò si verifica talvolta quando il codice nativo sottostante ha associato un comportamento speciale al ciclo di vita del parametro. Ad esempio, il distruttore per il parametro eseguirà un'azione di pulizia o eliminerà una risorsa preziosa.

Questo attributo informa il runtime che si desidera eliminare l'oggetto, se possibile, quando si torna a Objective-C dal metodo sovrascritto.

La regola è semplice: se il runtime deve creare una nuova rappresentazione gestita dall'oggetto nativo, alla fine della funzione, il conteggio di conservazione per l'oggetto nativo verrà eliminato e la proprietà Handle dell'oggetto gestito verrà cancellata. Ciò significa che se si mantiene un riferimento all'oggetto gestito, tale riferimento diventerà inutile (richiamando metodi su di esso verrà generata un'eccezione).

Se l'oggetto passato non è stato creato o se è già presente una rappresentazione gestita in sospeso dell'oggetto, l'eliminazione forzata non viene eseguita.

Attributi proprietà

NotImplementedAttribute

Questo attributo viene usato per supportare un Objective-C linguaggio in cui una proprietà con un getter viene introdotta in una classe di base e una sottoclasse modificabile introduce un setter.

Poiché C# non supporta questo modello, la classe di base deve avere sia il setter che il getter e una sottoclasse possono usare OverrideAttribute.

Questo attributo viene usato solo nei setter di proprietà e viene usato per supportare il linguaggio modificabile in Objective-C.

Esempio:

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

Attributi enumerazione

Il mapping NSString delle costanti ai valori di enumerazione è un modo semplice per creare un'API .NET migliore. IT:

  • consente di rendere più utile il completamento del codice, mostrando solo i valori corretti per l'API;
  • aggiunge sicurezza dei tipi, non è possibile utilizzare un'altra NSString costante in un contesto non corretto; e
  • consente di nascondere alcune costanti, rendendo il completamento del codice un elenco api più breve senza perdere funzionalità.

Esempio:

enum NSRunLoopMode {

    [DefaultEnumValue]
    [Field ("NSDefaultRunLoopMode")]
    Default,

    [Field ("NSRunLoopCommonModes")]
    Common,

    [Field (null)]
    Other = 1000
}

Dalla definizione di associazione precedente il generatore creerà se enum stesso e creerà anche un *Extensions tipo statico che include metodi di conversione bidirezionali tra i valori di enumerazione e le NSString costanti. Ciò significa che le costanti rimangono disponibili per gli sviluppatori anche se non fanno parte dell'API.

Esempi:

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

È possibile decorare un valore di enumerazione con questo attributo. Questo diventerà la costante restituita se il valore di enumerazione non è noto.

Nell'esempio precedente:

var x = (NSRunLoopMode) 99;
Call (x.GetConstant ()); // NSDefaultRunLoopMode will be used

Se non viene decorato alcun valore di enumerazione, verrà generata un'eccezione NotSupportedException .

ErrorDomainAttribute

I codici di errore vengono associati come valori di enumerazione. In genere è presente un dominio di errore e non è sempre facile trovare quale si applica (o se ne esiste anche uno).

È possibile usare questo attributo per associare il dominio di errore all'enumerazione stessa.

Esempio:

[Native]
[ErrorDomain ("AVKitErrorDomain")]
public enum AVKitError : nint {
    None = 0,
    Unknown = -1000,
    PictureInPictureStartFailed = -1001
}

È quindi possibile chiamare il metodo GetDomain di estensione per ottenere la costante del dominio di qualsiasi errore.

FieldAttribute

Si tratta dello stesso [Field] attributo usato per le costanti all'interno del tipo. Può essere usato anche all'interno di enumerazioni per eseguire il mapping di un valore con una costante specifica.

È possibile utilizzare un null valore per specificare quale valore di enumerazione deve essere restituito se viene specificata una nullNSString costante.

Nell'esempio precedente:

var constant = NSRunLoopMode.NewInWatchOS3; // will be null in watchOS 2.x
Call (NSRunLoopModeExtensions.GetValue (constant)); // will return 1000

Se non è presente alcun null valore, verrà generata un'eccezione ArgumentNullException .

Attributi globali

Gli attributi globali vengono applicati usando il [assembly:] modificatore di attributi come [LinkWithAttribute] o possono essere usati ovunque, ad esempio gli attributi di disponibilità.

LinkWithAttribute

Si tratta di un attributo a livello di assembly che consente agli sviluppatori di specificare i flag di collegamento necessari per riutilizzare una libreria associata senza forzare il consumer della libreria a configurare manualmente gli argomenti gcc_flags e mtouch aggiuntivi passati a una libreria.

Sintassi:

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

Questo attributo viene applicato a livello di assembly, ad esempio ciò che le associazioni CorePlot usano:

[assembly: LinkWith ("libCorePlot-CocoaTouch.a", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator, Frameworks = "CoreGraphics QuartzCore", ForceLoad = true)]

Quando si usa l'attributo , l'oggetto [LinkWith] specificato libraryName viene incorporato nell'assembly risultante, consentendo agli utenti di inviare una singola DLL che contiene sia le dipendenze non gestite che i flag della riga di comando necessari per utilizzare correttamente la libreria da Xamarin.iOS.

È anche possibile non specificare un oggetto libraryName, nel qual caso l'attributo LinkWith può essere usato solo per specificare flag del linker aggiuntivi:

[assembly: LinkWith (LinkerFlags = "-lsqlite3")]

Costruttori LinkWithAttribute

Questi costruttori consentono di specificare la libreria da collegare e incorporare nell'assembly risultante, le destinazioni supportate dalla libreria e tutti i flag di libreria facoltativi necessari per collegarsi alla libreria.

Si noti che l'argomento LinkTarget viene dedotto da Xamarin.iOS e non deve essere impostato.

Esempi:

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

La ForceLoad proprietà viene utilizzata per decidere se il -force_load flag di collegamento viene utilizzato o meno per il collegamento della libreria nativa. Per il momento, questo dovrebbe sempre essere vero.

LinkWithAttribute.Frameworks

Se la libreria associata ha un requisito rigido per qualsiasi framework (diverso Foundation da e UIKit), è necessario impostare la Frameworks proprietà su una stringa contenente un elenco delimitato da spazi dei framework della piattaforma necessari. Ad esempio, se si associa una libreria che richiede CoreGraphics e CoreText, impostare la Frameworks proprietà su "CoreGraphics CoreText".

LinkWithAttribute.IsCxx

Impostare questa proprietà su true se l'eseguibile risultante deve essere compilato usando un compilatore C++ anziché il valore predefinito, ovvero un compilatore C. Usare questa opzione se la libreria di cui si sta eseguendo l'associazione è stata scritta in C++.

LinkWithAttribute.LibraryName

Nome della libreria non gestita da aggregare. Si tratta di un file con estensione ".a" e può contenere codice oggetto per più piattaforme (ad esempio ARM e x86 per il simulatore).

Le versioni precedenti di Xamarin.iOS hanno controllato la LinkTarget proprietà per determinare la piattaforma supportata dalla libreria, ma questa operazione viene ora rilevata automaticamente e la LinkTarget proprietà viene ignorata.

LinkWithAttribute.LinkerFlags

La LinkerFlags stringa consente agli autori di associazioni di specificare eventuali flag del linker aggiuntivi necessari quando si collega la libreria nativa all'applicazione.

Ad esempio, se la libreria nativa richiede libxml2 e zlib, impostare la LinkerFlags stringa su "-lxml2 -lz".

LinkWithAttribute.LinkTarget

Le versioni precedenti di Xamarin.iOS hanno controllato la LinkTarget proprietà per determinare la piattaforma supportata dalla libreria, ma questa operazione viene ora rilevata automaticamente e la LinkTarget proprietà viene ignorata.

LinkWithAttribute.NeedsGccExceptionHandling

Impostare questa proprietà su true se la libreria collegata richiede la libreria GCC Exception Handling Library (gcc_eh)

La SmartLink proprietà deve essere impostata su true per consentire a Xamarin.iOS di determinare se ForceLoad è necessario o meno.

LinkWithAttribute.WeakFrameworks

La WeakFrameworks proprietà funziona allo stesso modo della Frameworks proprietà, ad eccezione del fatto che in fase di collegamento, l'identificatore -weak_framework viene passato a gcc per ognuno dei framework elencati.

WeakFrameworks consente alle librerie e alle applicazioni di collegarsi in modo debole ai framework della piattaforma in modo che possano usarli facoltativamente se sono disponibili, ma non accettano una dipendenza rigida da essi, utile se la libreria è destinata ad aggiungere funzionalità aggiuntive nelle versioni più recenti di iOS. Per altre informazioni sul collegamento debole, vedere la documentazione di Apple sul collegamento debole.

I candidati validi per il collegamento debole sono Frameworks account, CoreBluetooth, CoreImage, GLKitNewsstandKit e Twitter poiché sono disponibili solo in iOS 5.

AdviceAttribute

Usare questo attributo per offrire agli sviluppatori un suggerimento su altre API che potrebbero risultare più utili per l'uso. Ad esempio, se si fornisce una versione fortemente tipizzata di un'API, è possibile usare questo attributo sull'attributo con tipità debole per indirizzare lo sviluppatore all'API migliore.

Le informazioni di questo attributo sono mostrate nella documentazione e negli strumenti possono essere sviluppate per fornire suggerimenti agli utenti su come migliorare

RequiresSuperAttribute

Si tratta di una sottoclasse specializzata dell'attributo [Advice] che può essere usata per suggerire allo sviluppatore che esegue l'override di un metodo richiede una chiamata al metodo di base (sottoposto a override).

Corrisponde a clang__attribute__((objc_requires_super))

ZeroCopyStringsAttribute

Disponibile solo in Xamarin.iOS 5.4 e versioni successive.

Questo attributo indica al generatore che l'associazione per questa libreria specifica (se applicata con [assembly:]) o il tipo deve usare il marshalling rapido della stringa di copia zero. Questo attributo equivale a passare l'opzione --zero-copy della riga di comando al generatore.

Quando si usa zero-copy per le stringhe, il generatore usa in modo efficace la stessa stringa C# della stringa che Objective-C utilizza senza incorrere nella creazione di un nuovo NSString oggetto ed evitando di copiare i dati dalle stringhe C# alla Objective-C stringa. L'unico svantaggio dell'uso delle stringhe Zero Copy è che è necessario assicurarsi che qualsiasi proprietà stringa di cui si esegue il wrapping venga contrassegnata come retain o copy abbia impostato l'attributo [DisableZeroCopy] . Ciò è necessario perché l'handle per le stringhe di copia zero viene allocato nello stack e non è valido al momento della restituzione della funzione.

Esempio:

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

È anche possibile applicare l'attributo a livello di assembly e verrà applicato a tutti i tipi dell'assembly:

[assembly:ZeroCopyStrings]

Dizionari fortemente tipizzato

Con Xamarin.iOS 8.0 è stato introdotto il supporto per creare facilmente classi fortemente tipate per il wrapping NSDictionariesdi .

Sebbene sia sempre stato possibile usare il tipo di dati DictionaryContainer insieme a un'API manuale, è ora molto più semplice farlo. Per altre informazioni, vedere Surfacing Strong Types.For more information, see Surfacacing Strong Types.

StrongDictionary

Quando questo attributo viene applicato a un'interfaccia, il generatore produrrà una classe con lo stesso nome dell'interfaccia che deriva da DictionaryContainer e trasforma ogni proprietà definita nell'interfaccia in un getter fortemente tipizzato e setter per il dizionario.

In questo modo viene generata automaticamente una classe di cui è possibile creare un'istanza da un oggetto esistente NSDictionary o creato nuovo.

Questo attributo accetta un parametro, il nome della classe contenente le chiavi usate per accedere agli elementi nel dizionario. Per impostazione predefinita, ogni proprietà nell'interfaccia con l'attributo cercherà un membro nel tipo specificato per un nome con il suffisso "Key".

Ad esempio:

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

Nel caso precedente, la MyOption classe produrrà una proprietà stringa per Name che userà MyOptionKeys.NameKey come chiave nel dizionario per recuperare una stringa. E userà come MyOptionKeys.AgeKey chiave nel dizionario per recuperare un oggetto NSNumber che contiene un valore int.

Se si vuole usare una chiave diversa, è possibile usare l'attributo di esportazione nella proprietà , ad esempio:

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

Tipi di dizionario sicuro

Nella definizione sono supportati StrongDictionary i tipi di dati seguenti:

Tipo di interfaccia C# NSDictionarytipo Archiviazione
bool Boolean archiviato in un oggetto NSNumber
Valori di enumerazione integer archiviato in un oggetto NSNumber
int Intero a 32 bit archiviato in un NSNumber
uint Intero senza segno a 32 bit archiviato in un NSNumber
nint NSInteger archiviato in un oggetto NSNumber
nuint NSUInteger archiviato in un oggetto NSNumber
long Intero a 64 bit archiviato in un NSNumber
float Intero a 32 bit archiviato come NSNumber
double Intero a 64 bit archiviato come NSNumber
NSObject e sottoclassi NSObject
NSDictionary NSDictionary
string NSString
NSString NSString
C# Array di NSObject NSArray
C# Array di enumerazioni NSArray contenente NSNumber i valori