Guía de referencia de tipos de enlace
En este documento se describe la lista de atributos que puede usar para anotar los archivos de contrato de API para controlar el enlace y el código generado
Los contratos de API de Xamarin.iOS y Xamarin.Mac se escriben en C# principalmente como definiciones de interfaz que definen la forma en que Objective-C el código se expone a C#. El proceso implica una combinación de declaraciones de interfaz más algunas definiciones de tipos básicas que puede requerir el contrato de API. Para obtener una introducción a los tipos de enlace, consulte nuestra guía complementaria Bibliotecas de Objective-C enlaces.
Definiciones de tipo
Sintaxis:
[BaseType (typeof (BTYPE))
interface MyType : [Protocol1, Protocol2] {
IntPtr Constructor (string foo);
}
Cada interfaz de la definición del contrato que tiene el [BaseType]
atributo declara el tipo base para el objeto generado. En la declaración anterior, se generará un MyType
tipo de clase C# que se enlaza a un Objective-C tipo denominado MyType
.
Si especifica algún tipo después del typename (en el ejemplo anterior Protocol1
y Protocol2
) con la sintaxis de herencia de interfaz, el contenido de esas interfaces se insertará como si hubieran sido parte del contrato para MyType
.
La forma en que Xamarin.iOS expone que un tipo adopta un protocolo es mediante la inserción de todos los métodos y propiedades que se declararon en el protocolo en el propio tipo.
A continuación se muestra cómo se definiría la Objective-C declaración para UITextField
en un contrato de Xamarin.iOS:
@interface UITextField : UIControl <UITextInput> {
}
Se escribiría como un contrato de API de C#:
[BaseType (typeof (UIControl))]
interface UITextField : UITextInput {
}
Puede controlar muchos otros aspectos de la generación de código aplicando otros atributos a la interfaz, así como configurando el [BaseType]
atributo.
Generación de eventos
Una característica del diseño de la API de Xamarin.iOS y Xamarin.Mac es que asignamos Objective-C clases delegadas como eventos de C# y devoluciones de llamada. Los usuarios pueden elegir en una base por instancia si quieren adoptar el patrón de programación mediante la Objective-C asignación a propiedades como Delegate
la instancia de una clase que implementa los distintos métodos a los que llamaría el Objective-C tiempo de ejecución o eligiendo las propiedades y eventos de estilo C#.
Veamos un ejemplo de cómo usar el Objective-C modelo:
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 ();
}
}
En el ejemplo anterior, puede ver que hemos elegido sobrescribir dos métodos, una notificación de que se ha realizado en un evento de desplazamiento y el segundo que es una devolución de llamada que debe devolver un valor booleano que indica scrollView
si debe desplazarse hacia la parte superior o no.
El modelo de C# permite al usuario de la biblioteca escuchar notificaciones mediante la sintaxis de eventos de C# o la sintaxis de propiedad para enlazar devoluciones de llamada que se espera que devuelvan los valores.
Así es como el código de C# de la misma característica es similar al uso de lambdas:
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 ();
}
Dado que los eventos no devuelven valores (tienen un tipo de valor devuelto de void), puede conectar varias copias. No ShouldScrollToTop
es un evento, sino una propiedad con el tipo UIScrollViewCondition
que tiene esta firma:
public delegate bool UIScrollViewCondition (UIScrollView scrollView);
Devuelve un bool
valor, en este caso, la sintaxis lambda nos permite devolver el valor de la MakeDecision
función.
El generador de enlaces admite la generación de eventos y propiedades que vinculan una clase como UIScrollView
con su UIScrollViewDelegate
(llame bien a esta clase Model), esto se hace anotando la [BaseType]
definición con los Events
y Delegates
parámetros (descritos a continuación).
Además de anotar con [BaseType]
esos parámetros, es necesario informar al generador de algunos componentes más.
Para los eventos que toman más de un parámetro (en Objective-C la convención es que el primer parámetro de una clase de delegado es la instancia del objeto remitente) debe proporcionar el nombre que desea que sea la clase generada EventArgs
. Esto se hace con el [EventArgs]
atributo de la declaración de método en la clase Model. Por ejemplo:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
La declaración anterior generará una UIImagePickerImagePickedEventArgs
clase que deriva de EventArgs
y empaqueta ambos parámetros, el UIImage
y el NSDictionary
. El generador genera esto:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
A continuación, expone lo siguiente en la UIImagePickerController
clase :
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
Los métodos de modelo que devuelven un valor se enlazan de forma diferente. Estos requieren un nombre para el delegado de C# generado (la firma para el método) y también un valor predeterminado para devolver en caso de que el usuario no proporcione una implementación.
Por ejemplo, la ShouldScrollToTop
definición es esta:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIScrollViewDelegate {
[Export ("scrollViewShouldScrollToTop:"), DelegateName ("UIScrollViewCondition"), DefaultValue ("true")]
bool ShouldScrollToTop (UIScrollView scrollView);
}
El anterior creará un UIScrollViewCondition
delegado con la firma que se mostró anteriormente y, si el usuario no proporciona una implementación, el valor devuelto será true.
Además del [DefaultValue]
atributo, también puede usar el [DefaultValueFromArgument]
atributo que dirige al generador para devolver el valor del parámetro especificado en la llamada o el [NoDefaultValue]
parámetro que indica al generador que no hay ningún valor predeterminado.
BaseTypeAttribute
Sintaxis:
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
Use la Name
propiedad para controlar el nombre al que este tipo se enlazará en el Objective-C mundo. Normalmente se usa para asignar al tipo de C# un nombre compatible con las directrices de diseño de .NET Framework, pero que se asigna a un nombre en Objective-C que no sigue esa convención.
Por ejemplo, en el siguiente caso, asignamos el Objective-CNSURLConnection
tipo a NSUrlConnection
, ya que las instrucciones de diseño de .NET Framework usan "Url" en lugar de "URL":
[BaseType (typeof (NSObject), Name="NSURLConnection")]
interface NSUrlConnection {
}
El nombre especificado se usa como valor para el atributo generado [Register]
en el enlace. Si Name
no se especifica, el nombre corto del tipo se usa como valor para el [Register]
atributo en la salida generada.
BaseType.Events y BaseType.Delegates
Estas propiedades se usan para impulsar la generación de eventos de estilo C# en las clases generadas. Se usan para vincular una clase determinada con su Objective-C clase de delegado. Encontrará muchos casos en los que una clase usa una clase de delegado para enviar notificaciones y eventos. Por ejemplo, tendría BarcodeScanner
una clase complementaria BardodeScannerDelegate
. La BarcodeScanner
clase normalmente tendría una Delegate
propiedad a la que asignaría una instancia de BarcodeScannerDelegate
, mientras que esto funciona, es posible que quiera exponer a los usuarios una interfaz de eventos de estilo similar a C#, y en esos casos usaría las Events
y Delegates
propiedades del [BaseType]
atributo.
Estas propiedades siempre se establecen juntas y deben tener el mismo número de elementos y mantenerse sincronizados. La Delegates
matriz contiene una cadena para cada delegado débilmente tipado que desea encapsular, y la Events
matriz contiene un tipo para cada tipo que desea asociar.
[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
Si aplica este atributo cuando se crean nuevas instancias de esta clase, la instancia de ese objeto se mantendrá hasta que se haya invocado el método al KeepRefUntil
que hace referencia. Esto resulta útil para mejorar la facilidad de uso de las API, cuando no desea que el usuario mantenga una referencia a un objeto para usar el código. El valor de esta propiedad es el nombre de un método de la Delegate
clase, por lo que también debe usarlo en combinación con las Events
y Delegates
propiedades.
En el ejemplo siguiente se muestra cómo lo usa UIActionSheet
en 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
Cuando este atributo se aplica a la definición de interfaz, generará un [DesignatedInitializer]
atributo en el constructor predeterminado (generado), que se asigna al init
selector.
DisableDefaultCtorAttribute
Cuando este atributo se aplica a la definición de interfaz, impedirá que el generador genere el constructor predeterminado.
Use este atributo cuando necesite que el objeto se inicialice con uno de los otros constructores de la clase.
PrivateDefaultCtorAttribute
Cuando este atributo se aplica a la definición de interfaz, marcará el constructor predeterminado como privado. Esto significa que todavía se puede crear una instancia del objeto de esta clase internamente desde el archivo de extensión, pero simplemente no será accesible para los usuarios de la clase.
CategoryAttribute
Use este atributo en una definición de tipo para enlazar Objective-C categorías y exponerlos como métodos de extensión de C# para reflejar la forma Objective-C en que expone la funcionalidad.
Las categorías son un Objective-C mecanismo que se usa para ampliar el conjunto de métodos y propiedades disponibles en una clase. En la práctica, se usan para ampliar la funcionalidad de una clase base (por ejemplo NSObject
) cuando un marco específico está vinculado en (por ejemplo UIKit
), haciendo que sus métodos estén disponibles, pero solo si el nuevo marco está vinculado. En otros casos, se usan para organizar las características de una clase por funcionalidad. Son similares en espíritu a los métodos de extensión de C#.
Este es el aspecto de una categoría en Objective-C:
@interface UIView (MyUIViewExtension)
-(void) makeBackgroundRed;
@end
El ejemplo anterior se encuentra en una biblioteca que extendería instancias de UIView
con el método makeBackgroundRed
.
Para enlazarlos, puede usar el [Category]
atributo en una definición de interfaz. Cuando se usa el [Category]
atributo, el significado del [BaseType]
atributo cambia de usarse para especificar la clase base que se va a extender, a ser el tipo que se va a extender.
A continuación se muestra cómo UIView
se enlazan las extensiones y se convierten en métodos de extensión de C#:
[BaseType (typeof (UIView))]
[Category]
interface MyUIViewExtension {
[Export ("makeBackgroundRed")]
void MakeBackgroundRed ();
}
Lo anterior creará una MyUIViewExtension
clase que contenga el método de MakeBackgroundRed
extensión. Esto significa que ahora puede llamar MakeBackgroundRed
a en cualquier UIView
subclase, lo que le proporciona la misma funcionalidad que obtendría en Objective-C.
En algunos casos, encontrará miembros estáticos dentro de categorías como en el ejemplo siguiente:
@interface FooObject (MyFooObjectExtension)
+ (BOOL)boolMethod:(NSRange *)range;
@end
Esto provocará una definición incorrecta de la interfaz de C# de la categoría:
[Category]
[BaseType (typeof (FooObject))]
interface FooObject_Extensions {
// Incorrect Interface definition
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
Esto es incorrecto porque para usar la BoolMethod
extensión necesita una instancia de FooObject
pero está enlazando una extensión estática de ObjC, se trata de un efecto secundario debido al hecho de cómo se implementan los métodos de extensión de C#.
La única manera de usar las definiciones anteriores es mediante el siguiente código basura:
(null as FooObject).BoolMethod (range);
La recomendación para evitar esto es insertar la BoolMethod
definición dentro de FooObject
la propia definición de interfaz, lo que le permitirá llamar a esta extensión como se pretende FooObject.BoolMethod (range)
.
[BaseType (typeof (NSObject))]
interface FooObject {
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
Se emitirá una advertencia (BI1117) cada vez que se encuentre un [Static]
miembro dentro de una [Category]
definición. Si realmente desea tener [Static]
miembros dentro de las [Category]
definiciones, puede silenciar la advertencia mediante [Category (allowStaticMembers: true)]
o mediante adición de su definición de miembro o [Category]
interfaz con [Internal]
.
StaticAttribute
Cuando este atributo se aplica a una clase, simplemente generará una clase estática, una que no deriva de NSObject
, por lo que [BaseType]
se omite el atributo. Las clases estáticas se usan para hospedar variables públicas de C que desea exponer.
Por ejemplo:
[Static]
interface CBAdvertisement {
[Field ("CBAdvertisementDataServiceUUIDsKey")]
NSString DataServiceUUIDsKey { get; }
Generará una clase de C# con la API siguiente:
public partial class CBAdvertisement {
public static NSString DataServiceUUIDsKey { get; }
}
Definiciones de protocolo o modelo
Normalmente, la implementación del protocolo usa modelos. Difieren en que el tiempo de ejecución solo se registrará con Objective-C los métodos que realmente se han sobrescrito. De lo contrario, el método no se registrará.
Esto en general significa que cuando se subclase una clase que se ha marcado con ModelAttribute
, no debe llamar al método base. Al llamar a ese método, se producirá la siguiente excepción: Foundation.You_Should_Not_Call_base_In_This_Method. Se supone que debe implementar todo el comportamiento en la subclase para los métodos que invalide.
AbstractAttribute
De forma predeterminada, los miembros que forman parte de un protocolo no son obligatorios. Esto permite a los usuarios crear una subclase del Model
objeto simplemente derivando de la clase en C# e invalidando solo los métodos que les interesan. A veces, el Objective-C contrato requiere que el usuario proporcione una implementación para este método (los marcados con la @required
directiva en Objective-C). En esos casos, debe marcar esos métodos con el [Abstract]
atributo.
El [Abstract]
atributo se puede aplicar a métodos o propiedades y hace que el generador marque el miembro generado como abstracto y la clase para que sea una clase abstracta.
A continuación se toma de Xamarin.iOS:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UITableViewDataSource {
[Export ("tableView:numberOfRowsInSection:")]
[Abstract]
nint RowsInSection (UITableView tableView, nint section);
}
DefaultValueAttribute
Especifica el valor por defecto que debe devolver un método del modelo si el usuario no proporciona un método para este método en particular en el objeto Modelo
Sintaxis:
public class DefaultValueAttribute : Attribute {
public DefaultValueAttribute (object o);
public object Default { get; set; }
}
Por ejemplo, en la siguiente clase de delegado imaginaria para una Camera
clase, se proporciona una ShouldUploadToServer
clase que se expondría como una propiedad en la Camera
clase. Si el usuario de la Camera
clase no establece explícitamente un valor en una expresión lambda que pueda responder verdadero o falso, el valor predeterminado devuelto en este caso sería false, el valor especificado en el DefaultValue
atributo :
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("camera:shouldPromptForAction:"), DefaultValue (false)]
bool ShouldUploadToServer (Camera camera, CameraAction action);
}
Si el usuario establece un controlador en la clase imaginaria, este valor se omitiría:
var camera = new Camera ();
camera.ShouldUploadToServer = (camera, action) => return SomeDecision ();
Consulte también [NoDefaultValue]
, [DefaultValueFromArgument]
.
DefaultValueFromArgumentAttribute
Sintaxis:
public class DefaultValueFromArgumentAttribute : Attribute {
public DefaultValueFromArgumentAttribute (string argument);
public string Argument { get; }
}
Este atributo cuando se proporciona en un método que devuelve un valor en una clase de modelo indicará al generador que devuelva el valor del parámetro especificado si el usuario no proporcionó su propio método o expresión lambda.
Ejemplo:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, nfloat progress);
}
En el caso anterior si el usuario de la NSAnimation
clase eligió usar cualquiera de los eventos o propiedades de C# y no se estableció NSAnimation.ComputeAnimationCurve
a un método o lambda, el valor devuelto sería el valor pasado en el parámetro de progreso.
Consulte también: [NoDefaultValue]
, [DefaultValue]
IgnoredInDelegateAttribute
A veces tiene sentido no exponer una propiedad de evento o delegado de una clase Model a la clase host, por lo que agregar este atributo indicará al generador que evite la generación de cualquier método añadido con él.
[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
Este atributo se usa en los métodos Model que devuelven valores para establecer el nombre de la firma del delegado que se va a usar.
Ejemplo:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, float progress);
}
Con la definición anterior, el generador generará la siguiente declaración pública:
public delegate float NSAnimationProgress (MonoMac.AppKit.NSAnimation animation, float progress);
DelegateApiNameAttribute
Este atributo se usa para permitir que el generador cambie el nombre de la propiedad generada en la clase host. A veces resulta útil cuando el nombre del método de clase FooDelegate tiene sentido para la clase Delegate, pero tendría un aspecto extraño en la clase host como una propiedad.
También esto es realmente útil (y necesario) cuando tiene dos o más métodos de sobrecarga que tiene sentido mantenerlos denominados tal como está en la clase FooDelegate, pero desea exponerlos en la clase host con un nombre mejor determinado.
Ejemplo:
[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 definición anterior, el generador generará la siguiente declaración pública en la clase host:
public Func<NSAnimation, float, float> ComputeAnimationCurve { get; set; }
EventArgsAttribute
Para los eventos que toman más de un parámetro (en Objective-C la convención es que el primer parámetro de una clase de delegado es la instancia del objeto remitente) debe proporcionar el nombre que desea que sea la clase EventArgs generada. Esto se hace con el [EventArgs]
atributo de la declaración de método en Model
la clase.
Por ejemplo:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
La declaración anterior generará una UIImagePickerImagePickedEventArgs
clase que deriva de EventArgs y empaquetará ambos parámetros, de UIImage
y de NSDictionary
. El generador genera esto:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
A continuación, expone lo siguiente en la UIImagePickerController
clase :
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
EventNameAttribute
Este atributo se usa para permitir que el generador cambie el nombre de un evento o propiedad generado en la clase. A veces resulta útil cuando el nombre del método de clase Model tiene sentido para la clase de modelo, pero tendría un aspecto extraño en la clase de origen como evento o propiedad.
Por ejemplo, usa UIWebView
el siguiente bit de UIWebViewDelegate
:
[Export ("webViewDidFinishLoad:"), EventArgs ("UIWebView"), EventName ("LoadFinished")]
void LoadingFinished (UIWebView webView);
Lo anterior expone LoadingFinished
como método en UIWebViewDelegate
, pero LoadFinished
como el evento al que se va a enlazar en UIWebView
:
var webView = new UIWebView (...);
webView.LoadFinished += delegate { Console.WriteLine ("done!"); }
ModelAttribute
Cuando se aplica el [Model]
atributo a una definición de tipo en la API de contrato, el entorno de ejecución generará código especial que solo mostrará invocaciones a métodos de la clase si el usuario ha sobrescrito un método en la clase. Este atributo se suele aplicar a todas las API que encapsulan una Objective-C clase de delegado.
El runtime también generará una clase Objective-C que coincida con el nombre del protocolo correspondiente.
Es posible personalizar el nombre de la clase Objective-C de dos maneras:
Al configurar
AutoGeneratedName = true
:[Model (AutoGeneratedName = true)]
Esto hará que el tiempo de ejecución genere un nombre único para el tipo Objective-C. El nombre se basa actualmente en el nombre del ensamblado y en el nombre completo del tipo del modelo (esto puede cambiar en el futuro).
Al especificar explícitamente el nombre:
[Model (Name = "CustomName")]
Se recomienda usar AutoGeneratedName = true
. En .NET, el nombre siempre se genera (a menos que se especifique explícitamente como en 2. anterior) y la propiedad AutoGeneratedName
ya no existe.
NoDefaultValueAttribute
Especifica que el método del modelo no proporciona un valor devuelto predeterminado.
Esto funciona con el Objective-C tiempo de ejecución respondiendo false
a la solicitud en Objective-C tiempo de ejecución para determinar si el selector especificado se implementa en esta clase.
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("shouldDisplayPopup"), NoDefaultValue]
bool ShouldUploadToServer ();
}
Consulte también: [DefaultValue]
, [DefaultValueFromArgument]
Protocolos
El Objective-C concepto de protocolo no existe realmente en C#. Los protocolos son similares a las interfaces de C#, pero difieren en que no todos los métodos y propiedades declarados en un protocolo deben implementarse mediante la clase que lo adopta. En lugar de algunos de los métodos y propiedades, son opcionales.
Algunos protocolos se suelen usar como clases de modelo, que deben enlazarse mediante el [Model]
atributo.
[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 partir de Xamarin.iOS 7.0 se ha incorporado una nueva y mejorada funcionalidad de enlace de protocolos. Cualquier definición que contenga [Protocol]
el atributo generará en realidad tres clases de apoyo que mejoran enormemente la forma de consumir protocolos:
// 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);
}
}
La implementación de la clase proporciona una clase abstracta completa de la que puede sobrescribir métodos individuales y obtener una seguridad de tipo completa. Pero debido a que C# no admite varias herencias, hay escenarios en los que podría requerir una clase base diferente, pero aún desea implementar una interfaz.
Aquí es donde entra en juego la definición de interfaz generada. Es una interfaz que tiene todos los métodos necesarios del protocolo. Esto permite a los desarrolladores que quieran implementar el protocolo simplemente para implementar la interfaz. El tiempo de ejecución registrará automáticamente el tipo al adoptar el protocolo.
Observe que la interfaz solo enumera los métodos necesarios y expone los métodos opcionales. Esto significa que las clases que adopten el protocolo obtendrán una comprobación de tipos completa para los métodos obligatorios, pero tendrán que utilizar una tipificación débil (utilizando manualmente los atributos Export y comparando la firma) para los métodos opcionales del protocolo.
Para que sea conveniente consumir una API que use protocolos, la herramienta de enlace también generará una clase de método de extensiones que expone todos los métodos opcionales. Esto significa que, siempre que consuma una API, podrá tratar los protocolos como tener todos los métodos.
Si quiere usar las definiciones de protocolo en la API, deberá escribir interfaces vacías de esqueleto en la definición de API. Si desea usar MyProtocol en una API, deberá hacerlo:
[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 ();
}
Lo anterior es necesario porque en el momento del enlace no IMyProtocol
existiría, por eso es necesario proporcionar una interfaz vacía.
Adopción de interfaces generadas por protocolos
Siempre que implemente una de las interfaces generadas para los protocolos, de la siguiente manera:
class MyDelegate : NSObject, IUITableViewDelegate {
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
La implementación de los métodos de interfaz necesarios se exporta con el nombre adecuado, por lo que es equivalente a esto:
class MyDelegate : NSObject, IUITableViewDelegate {
[Export ("getRowHeight:")]
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
Esto funcionará para todos los miembros obligatorios del protocolo, pero hay que tener en cuenta un caso especial con los selectores opcionales.
Los miembros de protocolo opcionales se tratan de forma idéntica al usar la clase base:
public class UrlSessionDelegate : NSUrlSessionDownloadDelegate {
public override void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
pero cuando se usa la interfaz de protocolo, es necesario agregar [Export]. El IDE lo agregará a través de autocompletar al agregarlo a partir de la invalidación.
public class UrlSessionDelegate : NSObject, INSUrlSessionDownloadDelegate {
[Export ("URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:")]
public void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
Hay una ligera diferencia de comportamiento entre los dos en tiempo de ejecución:
- Los usuarios de la clase base (NSUrlSessionDownloadDelegate en el ejemplo) proporcionan todos los selectores obligatorios y opcionales, devolviendo valores predeterminados razonables.
- Los usuarios de la interfaz (INSUrlSessionDownloadDelegate en el ejemplo) solo responden a los selectores exactos proporcionados.
Algunas clases raras pueden comportarse de forma diferente aquí. En casi todos los casos, sin embargo, es seguro usar cualquiera de los dos.
Inserción de protocolos
Aunque enlaza los tipos existentes Objective-C que se han declarado como adopción de un protocolo, querrá insertar el protocolo directamente. Para ello, simplemente declare el protocolo como una interfaz sin ningún [BaseType]
atributo y enumere el protocolo en la lista de interfaces base para su interfaz.
Ejemplo:
interface SpeakProtocol {
[Export ("say:")]
void Say (string msg);
}
[BaseType (typeof (NSObject))]
interface Robot : SpeakProtocol {
[Export ("awake")]
bool Awake { get; set; }
}
Definiciones de miembros
Los atributos de esta sección se aplican a miembros individuales de un tipo: propiedades y declaraciones de método.
AlignAttribute
Se utiliza para especificar el valor de alineación para los tipos de devolución de propiedades. Algunas propiedades toman punteros a direcciones que deben alinearse en determinados límites (en Xamarin.iOS esto sucede, por ejemplo, con algunas GLKBaseEffect
propiedades que deben estar alineadas con 16 bytes). Puede usar esta propiedad para añadir el captador y usar el valor de alineación. Normalmente se usa con los tipos OpenTK.Vector4
y OpenTK.Matrix4
cuando se integran con Objective-C las API.
Ejemplo:
public interface GLKBaseEffect {
[Export ("constantColor")]
Vector4 ConstantColor { [Align (16)] get; set; }
}
AppearanceAttribute
El [Appearance]
atributo se limita a iOS 5, donde se introdujo el administrador de apariencias.
El [Appearance]
atributo se puede aplicar a cualquier método o propiedad que participe en el UIAppearance
marco de trabajo. Cuando este atributo se aplica a un método o propiedad de una clase, indicará al generador de enlaces que cree una clase de apariencia de tipo fuerte que se utilizará para aplicar estilo a todas las instancias de esta clase, o a las instancias que cumplan ciertos criterios.
Ejemplo:
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);
}
Lo anterior generaría el código siguiente en 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)
Utilice los métodos y propiedades en [AutoReleaseAttribute]
para ajustar la invocación al método en un NSAutoReleasePool
.
En Objective-C hay algunos métodos que devuelven valores que se agregan al valor predeterminado NSAutoReleasePool
. De forma predeterminada, estos irían al subproceso NSAutoReleasePool
, pero dado que Xamarin.iOS también mantiene una referencia a los objetos siempre que el objeto administrado resida, es posible que no desee mantener una referencia adicional en la NSAutoReleasePool
que solo se purgará hasta que el subproceso devuelva el control al siguiente subproceso o vuelva al bucle principal.
Este atributo se aplica por ejemplo en propiedades pesadas (por ejemplo UIImage.FromFile
) que devuelve objetos que se han agregado al valor predeterminado NSAutoReleasePool
. Sin este atributo, las imágenes se conservarían siempre y cuando el subproceso no devolviera el control al bucle principal. Si su hilo fuera una especie de descargador en segundo plano que siempre está vivo y a la espera de trabajo, las imágenes nunca saldrían a la luz.
ForcedTypeAttribute
El [ForcedTypeAttribute]
se usa para aplicar la creación de un tipo administrado aunque el objeto no administrado devuelto no coincida con el tipo descrito en la definición de enlace.
Esto resulta útil cuando el tipo descrito en un encabezado no coincide con el tipo de valor devuelto del método nativo, por ejemplo, tome la siguiente Objective-C definición de NSURLSession
:
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
Claramente indica que devolverá una NSURLSessionDownloadTask
instancia, pero sin embargo devuelve un NSURLSessionTask
, que es una superclase y, por tanto, no se puede convertir en NSURLSessionDownloadTask
. Puesto que estamos en un contexto seguro para tipos, se producirá una InvalidCastException
excepción.
Para cumplir con la descripción del encabezado y evitar, InvalidCastException
el [ForcedTypeAttribute]
se usa.
[BaseType (typeof (NSObject), Name="NSURLSession")]
interface NSUrlSession {
[Export ("downloadTaskWithRequest:")]
[return: ForcedType]
NSUrlSessionDownloadTask CreateDownloadTask (NSUrlRequest request);
}
El [ForcedTypeAttribute]
también acepta un valor booleano denominado Owns
que es false
de forma predeterminada [ForcedType (owns: true)]
. El parámetro owns se usa para seguir la directiva de propiedadpara los objetos Core Foundation.
El [ForcedTypeAttribute]
solo es válido en parámetros, propiedades y valor devuelto.
BindAsAttribute
El [BindAsAttribute]
permite enlazar NSNumber
,NSValue
y NSString
(enumeraciones) en tipos de C# más precisos. El atributo se puede usar para crear una API de .NET mejor, más precisa y precisa a través de la API nativa.
Puede añadir métodos (en valor devuelto), parámetros y propiedades con BindAs
. La única restricción es que el miembro no debe estar dentro de una [Protocol]
o [Model]
interfaz.
Por ejemplo:
[return: BindAs (typeof (bool?))]
[Export ("shouldDrawAt:")]
NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect);
Se generaría lo siguiente:
[Export ("shouldDrawAt:")]
bool? ShouldDraw (CGRect rect) { ... }
Internamente haremos las bool?
<->NSNumber
y CGRect
<->NSValue
conversiones.
Los tipos de encapsulación admitidos actuales son:
NSValue
NSNumber
NSString
NSValue
Se admiten los siguientes tipos de datos de C# para encapsularse desde o en NSValue
:
- CGAffineTransform
- NSRange
- CGVector
- SCNMatrix4
- CLLocationCoordinate2D
- SCNVector3
- SCNVector4
- CGPoint / PointF
- CGRect/RectangleF
- CGSize/SizeF
- UIEdgeInsets
- UIOffset
- MKCoordinateSpan
- CMTimeRange
- CMTime
- CMTimeMapping
- CATransform3D
NSNumber
Se admiten los siguientes tipos de datos de C# para encapsularse desde o en NSNumber
:
- bool
- byte
- doble
- FLOAT
- short
- int
- long
- sbyte
- ushort
- uint
- ulong
- nfloat
- nint
- nuint
- Enumeraciones
NSString
[BindAs]
funciona en conjunción con enumeraciones respaldadas por una constante NSString para que pueda crear una mejor API de .NET, por ejemplo:
[BindAs (typeof (CAScroll))]
[Export ("supportedScrollMode")]
NSString SupportedScrollMode { get; set; }
Se generaría lo siguiente:
[Export ("supportedScrollMode")]
CAScroll SupportedScrollMode { get; set; }
Controlaremos la enum
>-NSString
< conversión solo si el tipo de enumeración proporcionado a [BindAs]
está respaldado por una constante NSString.
Matrices
[BindAs]
también admite matrices de cualquiera de los tipos admitidos; puede tener la siguiente definición de API como ejemplo:
[return: BindAs (typeof (CAScroll []))]
[Export ("getScrollModesAt:")]
NSString [] GetScrollModes ([BindAs (typeof (CGRect []))] NSValue [] rects);
Se generaría lo siguiente:
[Export ("getScrollModesAt:")]
CAScroll? [] GetScrollModes (CGRect [] rects) { ... }
El rects
parámetro se encapsulará en un NSArray
que contiene un NSValue
para cada CGRect
uno y, de vuelta, obtendrá una matriz de CAScroll?
la que se ha creado con los valores del devuelto NSArray
que contiene NSStrings
.
BindAttribute
El [Bind]
atributo tiene dos usos uno cuando se aplica a una declaración de método o propiedad, y otro cuando se aplica al captador o establecedor individual en una propiedad.
Cuando se usa para un método o propiedad, el efecto del [Bind]
atributo es generar un método que invoca el selector especificado. Pero el método generado resultante no está añadido con el [Export]
atributo, lo que significa que no puede participar en la invalidación del método. Normalmente se usa en combinación con el [Target]
atributo para implementar Objective-C métodos de extensión.
Por ejemplo:
public interface UIView {
[Bind ("drawAtPoint:withFont:")]
SizeF DrawString ([Target] string str, CGPoint point, UIFont font);
}
Cuando se usa en un captador o establecedor, el [Bind]
atributo se usa para modificar los valores predeterminados inferidos por el generador de código al generar los nombres del selector de captador y Objective-C establecedor para una propiedad. De forma predeterminada, al marcar una propiedad con el nombre fooBar
, el generador generaría una fooBar
exportación para el captador y setFooBar:
para el establecedor. En algunos casos, Objective-C no sigue esta convención, normalmente cambian el nombre del captador a isFooBar
.
Usaría este atributo para informar al generador de esto.
Por ejemplo:
// Default behavior
[Export ("active")]
bool Active { get; set; }
// Custom naming with the Bind attribute
[Export ("visible")]
bool Visible { [Bind ("isVisible")] get; set; }
AsyncAttribute
Solo está disponible en Xamarin.iOS 6.3 y versiones posteriores.
Este atributo se puede aplicar a métodos que toman un controlador de finalización como último argumento.
Puede usar el [Async]
atributo en métodos cuyo último argumento es una devolución de llamada. Cuando se aplica a un método, el generador de enlaces generará una versión de ese método con el sufijo Async
. Si la devolución de llamada no toma parámetros, el valor devuelto será Task
, si la devolución de llamada toma un parámetro, el resultado será Task<T>
.
[Export ("upload:complete:")]
[Async]
void LoadFile (string file, NSAction complete)
Lo siguiente generará este método asincrónico:
Task LoadFileAsync (string file);
Si la devolución de llamada toma varios parámetros, debe establecer ResultType
o ResultTypeName
para especificar el nombre deseado del tipo generado que contendrá todas las propiedades.
delegate void OnComplete (string [] files, nint byteCount);
[Export ("upload:complete:")]
[Async (ResultTypeName="FileLoading")]
void LoadFiles (string file, OnComplete complete)
A continuación, se generará este método asincrónico, donde FileLoading
contiene propiedades para acceder a files
y byteCount
:
Task<FileLoading> LoadFile (string file);
Si el último parámetro de la devolución de llamada es, NSError
el método generado Async
comprobará si el valor no es null, y si es así, el método asincrónico generado establecerá la excepción de tarea.
[Export ("upload:onComplete:")]
[Async]
void Upload (string file, Action<string,NSError> onComplete);
Lo anterior genera el siguiente método asincrónico:
Task<string> UploadAsync (string file);
Y en caso de error, la tarea resultante tendrá la excepción establecida en un NSErrorException
que encapsula el resultante NSError
.
AsyncAttribute.ResultType
Utilice esta propiedad para especificar el valor del Task
objeto devuelto. Este parámetro toma un tipo existente, por lo que debe definirse en una de las definiciones de API principales.
AsyncAttribute.ResultTypeName
Utilice esta propiedad para especificar el valor del Task
objeto devuelto. Este parámetro toma el nombre del tipo deseado, el generador generará una serie de propiedades, una para cada parámetro que toma la devolución de llamada.
AsyncAttribute.MethodName
Use esta propiedad para personalizar el nombre de los métodos asincrónicos generados. El valor predeterminado es usar el nombre del método y anexar el texto "Async", puede usarlo para cambiar este valor predeterminado.
DesignatedInitializerAttribute
Cuando este atributo se aplica a un constructor, generará lo mismo [DesignatedInitializer]
en el ensamblado de la plataforma final. Esto sirve para ayudar al IDE a indicar qué constructor se debe usar en las subclases.
Esto debe asignarse al Objective-Cuso de /clang de __attribute__((objc_designated_initializer))
.
DisableZeroCopyAttribute
Este atributo se aplica a parámetros de cadena o propiedades de cadena e indica al generador de código que no use la serialización de cadenas de copia cero para este parámetro y, en su lugar, cree una nueva instancia de NSString a partir de la cadena de C#.
Este atributo solo es necesario en cadenas si indica al generador que use la serialización de cadenas de copia cero mediante la --zero-copy
opción de línea de comandos o estableciendo el atributo de nivel de ensamblado ZeroCopyStringsAttribute
.
Esto es necesario en los casos en los que la propiedad se declara en Objective-C para ser una propiedad retain
o assign
en lugar de una copy
propiedad. Normalmente, estos se producen en bibliotecas de terceros que los desarrolladores han "optimizado" de forma incorrecta. En general, retain
o assign
NSString
las propiedades son incorrectas, ya que NSMutableString
o las clases derivadas del usuario de NSString
pueden modificar el contenido de las cadenas sin el conocimiento del código de la biblioteca, lo que interrumpirá la aplicación de forma secundaria. Normalmente esto sucede debido a la optimización prematura.
A continuación se muestran dos propiedades de este tipo en Objective-C:
@property(nonatomic,retain) NSString *name;
@property(nonatomic,assign) NSString *name2;
DisposeAttribute
Cuando se aplica a [DisposeAttribute]
una clase, se proporciona un fragmento de código que se agregará a la Dispose()
implementación del método de la clase.
Dado que el método Dispose
es generado automáticamente por la herramienta bgen
, es necesario utilizar el atributo [Dispose]
para inyectar algo de código en la implementación del método Dispose
generado.
Por ejemplo:
[BaseType (typeof (NSObject))]
[Dispose ("if (OpenConnections > 0) CloseAllConnections ();")]
interface DatabaseConnection {
}
ExportAttribute
El [Export]
atributo se usa para marcar un método o propiedad que se va a exponer al Objective-C tiempo de ejecución. Este atributo se comparte entre la herramienta de enlace y los entornos de ejecución reales de Xamarin.iOS y Xamarin.Mac. En el caso de los métodos, el parámetro se pasa textualmente al código generado, para las propiedades, se generan exportaciones de captador y establecedor en función de la declaración base (vea la sección sobre para [BindAttribute]
obtener información sobre cómo modificar el comportamiento de la herramienta de enlace).
Sintaxis:
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; }
}
El selector representa el nombre del método o la propiedad subyacentes Objective-C que se enlaza.
ExportAttribute.ArgumentSemantic
FieldAttribute
Este atributo se usa para exponer una variable global de C como un campo que se carga a petición y se expone al código de C#. Normalmente, esto es necesario para obtener los valores de las constantes que se definen en C o Objective-C y que podrían ser tokens usados en algunas API, o cuyos valores son opacos y deben usarse tal cual por código de usuario.
Sintaxis:
public class FieldAttribute : Attribute {
public FieldAttribute (string symbolName);
public FieldAttribute (string symbolName, string libraryName);
public string SymbolName { get; set; }
public string LibraryName { get; set; }
}
El symbolName
es el símbolo de C con el que se va a vincular. De forma predeterminada, se cargará desde una biblioteca cuyo nombre se deduce del espacio de nombres donde se define el tipo. Si no es la biblioteca donde se busca el símbolo, debe pasar el libraryName
parámetro. Si va a vincular una biblioteca estática, use __Internal
como libraryName
parámetro.
Las propiedades generadas siempre son estáticas.
Las propiedades marcadas con el atributo Field pueden ser de los siguientes tipos:
NSString
NSArray
nint
/int
/long
nuint
/uint
/ulong
nfloat
/float
double
CGSize
System.IntPtr
- Enumeraciones
No se admiten establecedores para enumeraciones respaldadas por constantes NSString, pero se pueden enlazar manualmente si es necesario.
Ejemplo:
[Static]
interface CameraEffects {
[Field ("kCameraEffectsZoomFactorKey", "CameraLibrary")]
NSString ZoomFactorKey { get; }
}
InternalAttribute
El [Internal]
atributo se puede aplicar a métodos o propiedades y tiene el efecto de marcar el código generado con la internal
palabra clave de C# haciendo que el código solo sea accesible para el código en el ensamblado generado. Esto se utiliza normalmente para ocultar las API que son demasiado de bajo nivel o proporcionar una API pública subóptima que desea mejorar o para las API que no son compatibles con el generador y requieren algo de codificación manual.
Cuando diseñe el enlace, normalmente ocultará el método o la propiedad utilizando este atributo y proporcionará un nombre diferente para el método o la propiedad y, a continuación, en su archivo de soporte complementario de C#, añadirá una envoltura fuertemente tipado que exponga la funcionalidad subyacente.
Por ejemplo:
[Internal]
[Export ("setValue:forKey:")]
void _SetValueForKey (NSObject value, NSObject key);
[Internal]
[Export ("getValueForKey:")]
NSObject _GetValueForKey (NSObject key);
A continuación, en el archivo auxiliar, podría tener código similar al siguiente:
public NSObject this [NSObject idx] {
get {
return _GetValueForKey (idx);
}
set {
_SetValueForKey (value, idx);
}
}
IsThreadStaticAttribute
Este atributo marca el campo de respaldo de una propiedad que se va a anotar con el [ThreadStatic]
atributo .NET. Esto resulta útil si el campo es una variable estática de subproceso.
MarshalNativeExceptions (Xamarin.iOS 6.0.6)
Este atributo hará que un método admita excepciones nativas (Objective-C).
En lugar de llamar objc_msgSend
directamente, la invocación pasará por una trampoline personalizada que captura las excepciones de ObjectiveC y las serializa en excepciones administradas.
Actualmente solo se admiten algunas firmas objc_msgSend
(sabrá si no se admite una firma cuando la vinculación nativa de una aplicación que usa el enlace produce un error con un símbolo al le que falta el símbolo xamarin__objc_msgSend), pero se puede agregar más a petición.
NewAttribute
Este atributo se aplica a métodos y propiedades para que el generador genere la new
palabra clave delante de la declaración.
Se usa para evitar advertencias del compilador cuando se introduce el mismo nombre de propiedad o método en una subclase que ya existía en una clase base.
NotificationAttribute
Puede aplicar este atributo a los campos para que el generador genere una clase de notificaciones auxiliares fuertemente tipadas.
Este atributo se puede utilizar sin argumentos para las notificaciones que no llevan carga útil, o se puede especificar una System.Type
que haga referencia a otra interfaz en la definición de la API, normalmente con el nombre terminado en "EventArgs". El generador convertirá la interfaz en una clase que subclase EventArgs
e incluirá todas las propiedades enumeradas allí. El [Export]
atributo debe usarse en la EventArgs
clase para enumerar el nombre de la clave utilizada para buscar el Objective-C diccionario para capturar el valor.
Por ejemplo:
interface MyClass {
[Notification]
[Field ("MyClassDidStartNotification")]
NSString DidStartNotification { get; }
}
El código anterior generará una clase anidada MyClass.Notifications
con los métodos siguientes:
public class MyClass {
[..]
public Notifications {
public static NSObject ObserveDidStart (EventHandler<NSNotificationEventArgs> handler)
public static NSObject ObserveDidStart (NSObject objectToObserve, EventHandler<NSNotificationEventArgs> handler)
}
}
Los usuarios del código pueden suscribirse fácilmente a las notificaciones publicadas en NSDefaultCenter mediante código similar al siguiente:
var token = MyClass.Notifications.ObserverDidStart ((notification) => {
Console.WriteLine ("Observed the 'DidStart' event!");
});
O para establecer un objeto específico que se va a observar. Si pasa null
a objectToObserve
este método se comportará igual que su otro elemento del mismo nivel.
var token = MyClass.Notifications.ObserverDidStart (objectToObserve, (notification) => {
Console.WriteLine ("Observed the 'DidStart' event on objectToObserve!");
});
El valor devuelto de ObserveDidStart
se puede usar para dejar fácilmente de recibir notificaciones, de la siguiente manera:
token.Dispose ();
O bien, puede llamar a NSNotification.DefaultCenter.RemoveObserver y pasar el token. Si la notificación contiene parámetros, debe especificar una interfaz auxiliar, EventArgs
de la siguiente manera:
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; }
}
Lo anterior generará una MyScreenChangedEventArgs
clase con las propiedades ScreenX
y ScreenY
que capturarán los datos del diccionario NSNotification.UserInfo mediante las claves ScreenXKey
y ScreenYKey
, respectivamente, y aplicarán las conversiones adecuadas. El [ProbePresence]
atributo se usa para que el generador sondee si la clave está establecida en UserInfo
, en lugar de intentar extraer el valor. Esto se usa para los casos en los que la presencia de la clave es el valor (normalmente para valores booleanos).
Esto le permite escribir código similar al siguiente:
var token = MyClass.NotificationsObserveScreenChanged ((notification) => {
Console.WriteLine ("The new screen dimensions are {0},{1}", notification.ScreenX, notification.ScreenY);
});
En algunos casos, no hay ninguna constante asociada al valor pasado en el diccionario. Apple utiliza a veces constantes de símbolos públicos y a veces constantes de cadenas. De forma predeterminada, el [Export]
atributo de la clase proporcionada EventArgs
usará el nombre especificado como símbolo público para que se examine en tiempo de ejecución. Si este no es el caso y, en su lugar, se supone que se debe buscar como una constante de cadena, pase el ArgumentSemantic.Assign
valor al atributo Export.
Novedades de Xamarin.iOS 8.4
A veces, las notificaciones comenzarán a crearse sin ningún argumento, por lo que el uso de [Notification]
sin argumentos es aceptable. Pero a veces se introducirán parámetros para la notificación. Para admitir este escenario, el atributo se puede aplicar más de una vez.
Si va a desarrollar un enlace, y desea evitar interrumpir el código de usuario existente, activaría una notificación existente de:
interface MyClass {
[Notification]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
En una versión que muestra el atributo de notificación dos veces, de la siguiente manera:
interface MyClass {
[Notification]
[Notification (typeof (MyScreenChangedEventArgs)]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
NullAllowedAttribute
Cuando se aplica a una propiedad, marca la propiedad como permitir que el valor null
se le asigne. Esto solo es válido para los tipos de referencia.
Cuando se aplica a un parámetro en una firma de método, indica que el parámetro especificado puede ser nulo y que no debe realizarse ninguna comprobación para pasar null
valores.
Si el tipo de referencia no tiene este atributo, la herramienta de enlace generará una comprobación del valor que se va a asignar antes de pasarlo a Objective-C y generará una comprobación que producirá un ArgumentNullException
si el valor asignado es null
.
Por ejemplo:
// In properties
[NullAllowed]
UIImage IconFile { get; set; }
// In methods
void SetImage ([NullAllowed] UIImage image, State forState);
OverrideAttribute
Use este atributo para indicar al generador de enlaces para este método concreto se debe marcar con una override
palabra clave.
PreSnippetAttribute
Puede usar este atributo para insertar código que se va a insertar después de validar los parámetros de entrada, pero antes de que el código llame a en Objective-C.
Ejemplo:
[Export ("demo")]
[PreSnippet ("var old = ViewController;")]
void Demo ();
PrologueSnippetAttribute
Puede usar este atributo para insertar código que se va a insertar antes de que se valide cualquiera de los parámetros en el método generado.
Ejemplo:
[Export ("demo")]
[Prologue ("Trace.Entry ();")]
void Demo ();
PostGetAttribute
Indica al generador de enlaces que invoque la propiedad especificada de esta clase para obtener un valor de ella.
Esta propiedad se usa normalmente para actualizar la memoria caché que apunta a objetos de referencia que mantienen al gráfico de objetos al que se hace referencia. Normalmente se muestra en el código que tiene operaciones como Agregar o quitar. Este método se usa para que, después de agregar o quitar elementos, se actualice la memoria caché interna para asegurarnos de que estamos manteniendo referencias administradas a objetos que realmente están en uso. Esto es posible porque la herramienta de enlace genera un campo de respaldo para todos los objetos de referencia de un enlace determinado.
Ejemplo:
[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; }
}
En este caso, la Dependencies
propiedad se invocará después de agregar o quitar dependencias del NSOperation
objeto, lo que garantiza que tenemos un gráfico que representa los objetos cargados reales, lo que impide fugas de memoria, así como daños en la memoria.
PostSnippetAttribute
Puede usar este atributo para insertar código fuente de C# después de que el código haya invocado el Objective-C método subyacente
Ejemplo:
[Export ("demo")]
[PostSnippet ("if (old != null) old.DemoComplete ();")]
void Demo ();
ProxyAttribute
Este atributo se aplica para devolver valores para marcarlos como objetos proxy. Algunas Objective-C API devuelven objetos proxy que no se pueden diferenciar de los enlaces de usuario. El efecto de este atributo es marcar el objeto como un DirectBinding
objeto. Para ver un escenario en Xamarin.Mac, puede ver la explicación sobre este error.
ReleaseAttribute (Xamarin.iOS 6.0)
Esto se puede aplicar a los tipos devueltos para indicar que el generador debe llamar Release
al objeto antes de devolverlo. Esto sólo es necesario cuando un método le da un objeto retenido (a diferencia de un objeto auto-liberado, que es el escenario más común)
Ejemplo:
[Export ("getAndRetainObject")]
[return: Release ()]
NSObject GetAndRetainObject ();
Además, este atributo se propaga al código generado, de modo que el entorno de ejecución de Xamarin.iOS sepa que debe conservar el objeto al volver a Objective-C desde dicha función.
SealedAttribute
Indica al generador que marque el método generado como sellado. Si no se especifica este atributo, el valor predeterminado es generar un método virtual (ya sea un método virtual, un método abstracto o una invalidación en función de cómo se usen otros atributos).
StaticAttribute
Cuando el [Static]
atributo se aplica a un método o propiedad, esto genera un método estático o una propiedad. Si no se especifica este atributo, el generador genera un método o propiedad de instancia.
TransientAttribute
Use este atributo para marcar las propiedades cuyos valores son transitorios, es decir, los objetos creados temporalmente por iOS, pero que no son de larga duración. Cuando este atributo se aplica a una propiedad, el generador no crea un campo de respaldo para esta propiedad, lo que significa que la clase administrada no mantiene una referencia al objeto.
WrapAttribute
En el diseño de los enlaces de Xamarin.iOS/Xamarin.Mac, el [Wrap]
atributo se usa para encapsular un objeto de tipo débil con un objeto fuertemente tipado. Esto entra en juego principalmente con Objective-C objetos delegados que normalmente se declaran como de tipo id
o NSObject
. La convención que usa Xamarin.iOS y Xamarin.Mac es exponer esos delegados o orígenes de datos como de tipo NSObject
y se denominan mediante la convención "Débil" + el nombre que se expone. Una id delegate
propiedad de Objective-C se expondría como una NSObject WeakDelegate { get; set; }
propiedad en el archivo de contrato de API.
Pero normalmente el valor asignado a este delegado es de un tipo seguro, por lo que se muestra el tipo seguro y se aplica el [Wrap]
atributo, esto significa que los usuarios pueden optar por usar tipos débiles si necesitan algún control fino o si necesitan recurrir a trucos de bajo nivel, o pueden usar la propiedad fuertemente tipada para la mayoría de sus trabajos.
Ejemplo:
[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 ();
}
Así es como el usuario usaría la versión de tipo débil del delegado:
// The weak case, user has to roll his own
class SomeObject : NSObject {
[Export ("doDemo")]
void CallbackForDoDemo () {}
}
var demo = new Demo ();
demo.WeakDelegate = new SomeObject ();
Y así es como el usuario usaría la versión fuertemente tipada. Observe que el usuario aprovecha el sistema de tipos de C#, usa la palabra clave de remplazo para declarar su intención y no tiene que añadir manualmente el método con [Export]
, ya que hicimos ese trabajo en el enlace para el usuario:
// This is the strong case,
class MyDelegate : DemoDelegate {
override void Demo DoDemo () {}
}
var strongDemo = new Demo ();
demo.Delegate = new MyDelegate ();
Otro uso del [Wrap]
atributo es admitir la versión fuertemente tipada de métodos. Por ejemplo:
[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);
}
Cuando el [Wrap]
atributo se aplica en un método dentro de un tipo añadido con un [Category]
atributo, debe incluir This
como primer argumento, ya que se genera un método de extensión. Por ejemplo:
[Wrap ("Write (This, image, options?.Dictionary, out error)")]
bool Write (CIImage image, CIImageRepresentationOptions options, out NSError error);
Los miembros generados por [Wrap]
no virtual
son de forma predeterminada, si necesita un virtual
miembro, puede establecerlo en el parámetro true
opcional isVirtual
.
[BaseType (typeof (NSObject))]
interface FooExplorer {
[Export ("fooWithContentsOfURL:")]
void FromUrl (NSUrl url);
[Wrap ("FromUrl (NSUrl.FromString (url))", isVirtual: true)]
void FromUrl (string url);
}
[Wrap]
también se puede usar directamente en captadores de propiedades y establecedores.
Esto permite tener control total sobre ellos y ajustar el código según sea necesario.
Por ejemplo, considere la siguiente definición de API que usa enumeraciones inteligentes:
// Smart enum.
enum PersonRelationship {
[Field (null)]
None,
[Field ("FMFather", "__Internal")]
Father,
[Field ("FMMother", "__Internal")]
Mother
}
Definición de interfaz:
// Property definition.
[Export ("presenceType")]
NSString _PresenceType { get; set; }
PersonRelationship PresenceType {
[Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType)")]
get;
[Wrap ("_PresenceType = value.GetConstant ()")]
set;
}
Atributos de parámetro
En esta sección se describen los atributos que se pueden aplicar a los parámetros de una definición de método, así como a los [NullAttribute]
que se aplican a una propiedad en su conjunto.
BlockCallback
Este atributo se aplica a los tipos de parámetro en las declaraciones de delegado de C# para notificar al enlazador que el parámetro en cuestión se ajusta a la Objective-C convención de llamada de bloque y debe serializarlo de esta forma.
Normalmente se usa para devoluciones de llamada definidas de la siguiente manera en Objective-C:
typedef returnType (^SomeTypeDefinition) (int parameter1, NSString *parameter2);
Vea también: CCallback.
CCallback
Este atributo se aplica a los tipos de parámetro de las declaraciones de delegado de C# para notificar al enlazador que el parámetro en cuestión se ajusta a la convención de llamada de la función ABI de C y debe serializarlo de esta forma.
Normalmente se usa para devoluciones de llamada definidas de la siguiente manera en Objective-C:
typedef returnType (*SomeTypeDefinition) (int parameter1, NSString *parameter2);
Consulte también: BlockCallback.
Parámetros
Puede usar el [Params]
atributo en el último parámetro de matriz de una definición de método para que el generador inserte un "parámetro" en la definición. Esto permite que la enlace admita fácilmente parámetros opcionales.
Por ejemplo, la siguiente definición:
[Export ("loadFiles:")]
void LoadFiles ([Params]NSUrl [] files);
Permite escribir el código siguiente:
foo.LoadFiles (new NSUrl (url));
foo.LoadFiles (new NSUrl (url1), new NSUrl (url2), new NSUrl (url3));
Esto tiene la ventaja agregada de que no requiere que los usuarios creen una matriz únicamente para pasar elementos.
PlainString
Puede usar el [PlainString]
atributo delante de los parámetros de cadena para indicar al generador de enlaces que pase la cadena como una cadena C, en lugar de pasar el parámetro como NSString
.
La mayoría de las Objective-C API consumen NSString
parámetros, pero una serie de API exponen una char *
API para pasar cadenas, en lugar de la NSString
variación.
En esos casos, use [PlainString]
.
Por ejemplo, las siguientes Objective-C declaraciones son válidas:
- (void) setText: (NSString *) theText;
- (void) logMessage: (char *) message;
Debe enlazarse de esta manera:
[Export ("setText:")]
void SetText (string theText);
[Export ("logMessage:")]
void LogMessage ([PlainString] string theText);
RetainAttribute
Indica al generador que mantenga una referencia al parámetro especificado. El generador proporcionará la memoria auxiliar para este campo o puede especificar un nombre (el WrapName
) en el que almacenar el valor. Esto resulta útil para contener una referencia a un objeto administrado que se pasa como parámetro a Objective-C y cuando sepa que Objective-C solo mantendrá esta copia del objeto. Por ejemplo, una API similar SetDisplay (SomeObject)
usaría este atributo, ya que es probable que SetDisplay solo pudiera mostrar un objeto a la vez. Si necesita realizar un seguimiento de más de un objeto (por ejemplo, para una API similar a stack), usaría el [RetainList]
atributo.
Sintaxis:
public class RetainAttribute {
public RetainAttribute ();
public RetainAttribute (string wrapName);
public string WrapName { get; }
}
TransientAttribute
Este atributo se aplica a los parámetros y solo se usa al realizar la transición de Objective-C a C#. Durante esas transiciones, los distintos Objective-CNSObject
parámetros se encapsulan en una representación administrada del objeto.
El tiempo de ejecución tomará una referencia al objeto nativo y la mantendrá hasta que desaparezca la última referencia gestionada al objeto, y la GC tenga la oportunidad de ejecutarse.
En algunos casos, es importante que el entorno de ejecución de C# no mantenga una referencia al objeto nativo. Esto sucede a veces cuando el código nativo subyacente ha asociado un comportamiento especial al ciclo de vida del parámetro. Por ejemplo: el destructor del parámetro realizará alguna acción de limpieza, o dispondrá de algún recurso preciado.
Este atributo informa al tiempo de ejecución que usted desea que el objeto sea desechado si es posible al volver a Objective-C desde su método sobrescrito.
La regla es sencilla: si el tiempo de ejecución ha tenido que crear una nueva representación gestionada a partir del objeto nativo, al final de la función se eliminará el recuento de retenciones del objeto nativo y se borrará la propiedad Handle del objeto gestionado. Esto significa que si mantiene una referencia al objeto administrado, esa referencia se volverá inútil (invocar métodos en él producirá una excepción).
Si no se creó el objeto pasado o si ya había una representación administrada pendiente del objeto, no se realiza la eliminación forzada.
Atributos de propiedades
NotImplementedAttribute
Este atributo se usa para admitir un Objective-C lenguaje donde se introduce una propiedad con un captador en una clase base y una subclase mutable introduce un establecedor.
Dado que C# no admite este modelo, la clase base debe tener el establecedor y el captador, y una subclase puede usar OverrideAttribute.
Este atributo solo se usa en establecedores de propiedades, y se usa para admitir la expresión mutable en Objective-C.
Ejemplo:
[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; }
}
Atributos de enumeración
La asignación de NSString
constantes a valores de enumeración es una manera fácil de crear una mejor API de .NET. Este:
- permite que la finalización del código sea más útil, mostrando solo los valores correctos para la API;
- agrega seguridad de tipos, no puede usar otra
NSString
constante en un contexto incorrecto; y - permite ocultar algunas constantes, lo que hace que la finalización del código muestre una lista de API más corta sin perder la funcionalidad.
Ejemplo:
enum NSRunLoopMode {
[DefaultEnumValue]
[Field ("NSDefaultRunLoopMode")]
Default,
[Field ("NSRunLoopCommonModes")]
Common,
[Field (null)]
Other = 1000
}
A partir de la definición de enlace anterior, el generador creará el enum
propio y también creará un *Extensions
tipo estático que incluye métodos de conversión de dos maneras entre los valores de enumeración y las NSString
constantes. Esto significa que las constantes permanecen disponibles para los desarrolladores incluso si no forman parte de la API.
Ejemplos:
// 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
Puede añadir un valor de enumeración con este atributo. Se convertirá en la constante que se devuelve si no se conoce el valor de enumeración.
En el ejemplo anterior:
var x = (NSRunLoopMode) 99;
Call (x.GetConstant ()); // NSDefaultRunLoopMode will be used
Si no hay ningún valor de enumeración añadido, se producirá una NotSupportedException
excepción.
ErrorDomainAttribute
Los códigos de error se enlazan como valores de enumeración. Por lo general, hay un dominio de error para ellos y no siempre es fácil encontrar cuál se aplica (o si existe uno).
Puede usar este atributo para asociar el dominio de error a la propia enumeración.
Ejemplo:
[Native]
[ErrorDomain ("AVKitErrorDomain")]
public enum AVKitError : nint {
None = 0,
Unknown = -1000,
PictureInPictureStartFailed = -1001
}
A continuación, puede llamar al método de extensión GetDomain
para obtener la constante de dominio de cualquier error.
FieldAttribute
Este es el mismo [Field]
atributo que se usa para las constantes dentro del tipo. También se puede usar dentro de enumeraciones para asignar un valor con una constante específica.
Se puede usar un null
valor para especificar qué valor de enumeración se debe devolver si se especifica una null
NSString
constante.
En el ejemplo anterior:
var constant = NSRunLoopMode.NewInWatchOS3; // will be null in watchOS 2.x
Call (NSRunLoopModeExtensions.GetValue (constant)); // will return 1000
Si no hay ningún null
valor presente, se producirá una ArgumentNullException
excepción.
Atributos globales
Los atributos globales se aplican mediante un modificador de atributo [assembly:]
, como [LinkWithAttribute]
, o se pueden usar en cualquier lugar, como los atributos de disponibilidad.
LinkWithAttribute
Se trata de un atributo de nivel de ensamblado que permite a los desarrolladores especificar las marcas de vinculación necesarias para reutilizar una biblioteca enlazada sin forzar al consumidor de la biblioteca a configurar manualmente los argumentos gcc_flags y mtouch adicionales pasados a una biblioteca.
Sintaxis:
// 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; }
}
Este atributo se aplica en el nivel de ensamblado, por ejemplo, esto es lo que usan los enlaces CorePlot:
[assembly: LinkWith ("libCorePlot-CocoaTouch.a", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator, Frameworks = "CoreGraphics QuartzCore", ForceLoad = true)]
Cuando se usa el [LinkWith]
atributo, especificado libraryName
se inserta en el ensamblado resultante, lo que permite a los usuarios enviar un único archivo DLL que contenga las dependencias no administradas, así como las marcas de línea de comandos necesarias para consumir correctamente la biblioteca de Xamarin.iOS.
También es posible no proporcionar un libraryName
, en cuyo caso el LinkWith
atributo solo se puede usar para especificar marcas adicionales del enlazador:
[assembly: LinkWith (LinkerFlags = "-lsqlite3")]
Constructores LinkWithAttribute
Estos constructores permiten especificar la biblioteca con la que vincular e insertar en el ensamblado resultante, los destinos admitidos que sean compatibles con la biblioteca y las marcas de biblioteca opcionales necesarias para vincularla.
Tenga en cuenta que LinkTarget
Xamarin.iOS deduce el argumento y no es necesario establecerlo.
Ejemplos:
// 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
propiedad se usa para decidir si se usa o no la -force_load
marca de vínculo, para vincular la biblioteca nativa. Por ahora, esto siempre debe ser verdadero.
LinkWithAttribute.Frameworks
Si la biblioteca enlazada tiene un requisito estricto en cualquier marco (distinto Foundation
de y UIKit
), debe establecer la Frameworks
propiedad en una cadena que contenga una lista delimitada por espacios de los marcos de plataforma necesarios. Por ejemplo, si va a enlazar una biblioteca que requiere CoreGraphics
y CoreText
, establecería la Frameworks
propiedad en "CoreGraphics CoreText"
.
LinkWithAttribute.IsCxx
Establezca esta propiedad en verdadero si el ejecutable resultante debe compilarse mediante un compilador de C++ en lugar del valor predeterminado, que es un compilador de C. Úselo si la biblioteca que está enlazando se escribió en C++.
LinkWithAttribute.LibraryName
Nombre de la biblioteca no administrada que se va a agrupar. Se trata de un archivo con la extensión ".a" y puede contener código de objeto para varias plataformas (por ejemplo, ARM y x86 para el simulador).
Las versiones anteriores de Xamarin.iOS comprobaron la LinkTarget
propiedad para determinar la plataforma que admitía la biblioteca, pero ahora se detecta automáticamente y se LinkTarget
omite la propiedad.
LinkWithAttribute.LinkerFlags
La LinkerFlags
cadena proporciona una manera de que los autores de enlaces especifiquen las marcas de enlazador adicionales necesarias al vincular la biblioteca nativa a la aplicación.
Por ejemplo, si la biblioteca nativa requiere libxml2 y zlib, establecería la LinkerFlags
cadena en "-lxml2 -lz"
.
LinkWithAttribute.LinkTarget
Las versiones anteriores de Xamarin.iOS comprobaron la LinkTarget
propiedad para determinar la plataforma que admitía la biblioteca, pero ahora se detecta automáticamente y se LinkTarget
omite la propiedad.
LinkWithAttribute.NeedsGccExceptionHandling
Establezca esta propiedad en verdadero si la biblioteca que está vinculando requiere la biblioteca de control de excepciones de GCC (gcc_eh)
LinkWithAttribute.SmartLink
La SmartLink
propiedad debe establecerse en verdadero para permitir que Xamarin.iOS determine si ForceLoad
es necesario o no.
LinkWithAttribute.WeakFrameworks
La WeakFrameworks
propiedad funciona de la misma manera que la Frameworks
propiedad, salvo que en el momento del vínculo, el -weak_framework
especificador se pasa a gcc para cada uno de los marcos enumerados.
WeakFrameworks
hace posible que las bibliotecas y aplicaciones se vinculen débilmente con los marcos de trabajo de la plataforma para que puedan utilizarlos opcionalmente si están disponibles, pero sin depender de ellos, lo que resulta útil si su biblioteca está destinada a añadir funciones adicionales en versiones más recientes de iOS. Para obtener más información sobre la vinculación débil, consulte la documentación de Apple sobre La vinculación débil.
Los buenos candidatos para la vinculación débil serían Frameworks
como Cuentas, CoreBluetooth
, CoreImage
, GLKit
, NewsstandKit
, y Twitter
ya que solo están disponibles en iOS 5.
AdviceAttribute
Use este atributo para proporcionar a los desarrolladores una sugerencia sobre otras API que podrían resultar más convenientes para su uso. Por ejemplo, si proporciona una versión fuertemente tipada de una API, podría usar este atributo en el atributo débilmente tipado para dirigir al desarrollador a la mejor API.
La información de este atributo se muestra en la documentación y las herramientas para proporcionar sugerencias de usuario sobre cómo mejorar
RequiresSuperAttribute
Se trata de una subclase especializada del [Advice]
atributo que se puede usar para sugerir al desarrollador que invalida un método requiere una llamada al método base (invalidado).
Esto corresponde a clang
__attribute__((objc_requires_super))
ZeroCopyStringsAttribute
Solo está disponible en Xamarin.iOS 5.4 y versiones posteriores.
Este atributo indica al generador que el enlace para esta biblioteca específica (si se aplica con [assembly:]
) o tipo debe usar la serialización rápida de cadenas de copia cero. Este atributo es equivalente a pasar la opción --zero-copy
de línea de comandos al generador.
Cuando se usa una copia cero para cadenas, el generador usa eficazmente la misma cadena de C# que la cadena que Objective-C consume sin incurrir en la creación de un nuevo NSString
objeto y evitando copiar los datos de las cadenas de C# en la Objective-C cadena. El único inconveniente de usar cadenas de copia cero es que debe asegurarse de que cualquier propiedad de cadena encapsulada que se marque como retain
o copy
tenga el conjunto de [DisableZeroCopy]
atributos. Esto es necesario porque el identificador de las cadenas de copia cero se asigna en la pila y no es válido al devolver la función.
Ejemplo:
[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; }
}
También puede aplicar el atributo en el nivel de ensamblado y se aplicará a todos los tipos del ensamblado:
[assembly:ZeroCopyStrings]
Diccionarios fuertemente tipados
Con Xamarin.iOS 8.0 hemos introducido compatibilidad para crear fácilmente clases fuertemente tipadas que encapsulan NSDictionaries
.
Aunque siempre ha sido posible usar el tipo de datos DictionaryContainer junto con una API manual, ahora es mucho más sencillo hacerlo. Para obtener más información, vea Surfacing Strong Types.
StrongDictionary
Cuando este atributo se aplica a una interfaz, el generador generará una clase con el mismo nombre que la interfaz que deriva de DictionaryContainer y convierte cada propiedad definida en la interfaz en un captador fuertemente tipado y establecedor para el diccionario.
Esto genera automáticamente una clase que se puede crear a partir de una existente NSDictionary
o que se ha creado.
Este atributo toma un parámetro, el nombre de la clase que contiene las claves que se usan para tener acceso a los elementos del diccionario. De forma predeterminada, cada propiedad de la interfaz con el atributo buscará un miembro en el tipo especificado para un nombre con el sufijo "Key".
Por ejemplo:
[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; }
}
En el caso anterior, la MyOption
clase generará una propiedad de cadena para Name
que use como MyOptionKeys.NameKey
clave en el diccionario para recuperar una cadena. Y usará MyOptionKeys.AgeKey
como clave en el diccionario para recuperar un NSNumber
objeto que contiene un valor int.
Si desea usar una clave diferente, puede usar el atributo exportar en la propiedad, por ejemplo:
[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; }
}
Tipos de diccionario seguros
Los siguientes tipos de datos se admiten en la StrongDictionary
definición de:
Tipo de interfaz de C# | NSDictionary Tipo de almacenamiento |
---|---|
bool |
Boolean almacenado en un NSNumber |
Valores de enumeración | entero almacenado en un NSNumber |
int |
Entero de 32 bits almacenado en un NSNumber |
uint |
Entero sin signo de 32 bits almacenado en un NSNumber |
nint |
NSInteger almacenado en un NSNumber |
nuint |
NSUInteger almacenado en un NSNumber |
long |
Entero de 64 bits almacenado en un NSNumber |
float |
Entero de 32 bits almacenado como un NSNumber |
double |
Entero de 64 bits almacenado como un NSNumber |
NSObject y subclases |
NSObject |
NSDictionary |
NSDictionary |
string |
NSString |
NSString |
NSString |
C# Array de NSObject |
NSArray |
C# Array de enumeraciones |
NSArray contiene NSNumber valores |