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 NSObject
UIKit
), 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 , ModelAttribute
non è 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 UIImage
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; }
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 UIWebView
oggetto :
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:
Impostazione
AutoGeneratedName = true
di :[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).
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 NSURLSessionTask
oggetto , 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 , InvalidCastException
viene 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]
NSNumber
e 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à , Task
se 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 byteCount
a :
Task<FileLoading> LoadFile (string file);
Se l'ultimo parametro del callback è , NSError
il 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 ZeroCopyStringsAttribute
a 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 assign
NSString
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 isVirtual
virtual
.
[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 null
NSString
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)
LinkWithAttribute.SmartLink
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
, GLKit
NewsstandKit
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 NSDictionaries
di .
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# | NSDictionary Tipo di 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 |