Guia de referência de tipos de associação
Este documento descreve a lista de atributos que você pode usar para anotar seus arquivos de contrato de API para conduzir a vinculação e o código gerado
Os contratos de API do Xamarin.iOS e do Xamarin.Mac são escritos em C# principalmente como definições de interface que definem a maneira como Objective-C o código é exibido para C#. O processo envolve uma combinação de declarações de interface mais algumas definições básicas de tipo que o contrato de API pode exigir. Para obter uma introdução aos tipos de associação, consulte nosso guia complementar Bibliotecas de associaçãoObjective-C.
Definições de tipo
Sintaxe:
[BaseType (typeof (BTYPE))
interface MyType : [Protocol1, Protocol2] {
IntPtr Constructor (string foo);
}
Cada interface em sua definição de contrato que tem o [BaseType]
atributo declara o tipo base para o objeto gerado. Na declaração acima, será gerado um MyType
tipo de classe C# que se associa a um Objective-C tipo chamado MyType
.
Se você especificar qualquer tipo após o typename (no exemplo acima Protocol1
e ) usando a sintaxe Protocol2
de herança de interface, o conteúdo dessas interfaces será embutido como se fizesse parte do contrato para MyType
.
A maneira como o Xamarin.iOS mostra que um tipo adota um protocolo é embutindo todos os métodos e propriedades que foram declarados no protocolo no próprio tipo.
O seguinte mostra como a Objective-C declaração for UITextField
seria definida em um contrato Xamarin.iOS:
@interface UITextField : UIControl <UITextInput> {
}
Seria escrito assim como um contrato de API C#:
[BaseType (typeof (UIControl))]
interface UITextField : UITextInput {
}
Você pode controlar muitos outros aspectos da geração de código aplicando outros atributos à interface, bem como configurando o [BaseType]
atributo.
Gerando eventos
Um recurso do design da API do Xamarin.iOS e do Xamarin.Mac é que mapeamos Objective-C classes de delegado como eventos e retornos de chamada C#. Os usuários podem escolher por instância se desejam adotar o Objective-C padrão de programação, atribuindo propriedades como Delegate
uma instância de uma classe que implementa os vários métodos que o Objective-C runtime chamaria ou escolhendo os eventos e propriedades no estilo C#.
Vejamos um exemplo de como usar o 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 ();
}
}
No exemplo acima, você pode ver que optamos por substituir dois métodos, um é uma notificação de que um evento de rolagem ocorreu e o segundo é um retorno de chamada que deve retornar um valor booleano instruindo se scrollView
ele deve rolar para o topo ou não.
O modelo C# permite que o usuário da sua biblioteca ouça notificações usando a sintaxe de evento C# ou a sintaxe de propriedade para conectar retornos de chamada que devem retornar valores.
É assim que o código C# para o mesmo recurso se parece usando 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 ();
}
Como os eventos não retornam valores (eles têm um tipo de retorno void), você pode conectar várias cópias. O ShouldScrollToTop
não é um evento, é uma propriedade com o tipo UIScrollViewCondition
que tem esta assinatura:
public delegate bool UIScrollViewCondition (UIScrollView scrollView);
Ele retorna um bool
valor, neste caso, a sintaxe lambda nos permite apenas retornar o valor da MakeDecision
função.
O gerador de associação dá suporte à geração de eventos e propriedades que vinculam uma classe como UIScrollView
à sua UIScrollViewDelegate
(bem, chame-os de classe Model), isso é feito anotando sua [BaseType]
definição com os Events
parâmetros e Delegates
(descritos abaixo).
Além de anotar o [BaseType]
com esses parâmetros, é necessário informar ao gerador mais alguns componentes.
Para eventos que usam mais de um parâmetro (na Objective-C convenção é que o primeiro parâmetro em uma classe delegada é a instância do objeto remetente), você deve fornecer o nome que deseja que a classe gerada EventArgs
seja. Isso é feito com o [EventArgs]
atributo na declaração do método em sua classe Model. Por exemplo:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
A declaração acima gerará uma UIImagePickerImagePickedEventArgs
classe que deriva e EventArgs
empacota ambos os parâmetros, o e o UIImage
NSDictionary
. O gerador produz isso:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
Em seguida, ele expõe o UIImagePickerController
seguinte na classe:
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
Os métodos de modelo que retornam um valor são associados de forma diferente. Eles exigem um nome para o delegado C# gerado (a assinatura do método) e também um valor padrão a ser retornado caso o usuário não forneça uma implementação.
Por exemplo, a ShouldScrollToTop
definição é esta:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIScrollViewDelegate {
[Export ("scrollViewShouldScrollToTop:"), DelegateName ("UIScrollViewCondition"), DefaultValue ("true")]
bool ShouldScrollToTop (UIScrollView scrollView);
}
O acima criará um UIScrollViewCondition
delegado com a assinatura mostrada acima e, se o usuário não fornecer uma implementação, o valor retornado será true.
Além do [DefaultValue]
atributo, você também pode usar o [DefaultValueFromArgument]
atributo que direciona o gerador para retornar o valor do parâmetro especificado na chamada ou o [NoDefaultValue]
parâmetro que instrui o gerador de que não há valor padrão.
Atributo BaseType
Sintaxe:
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
Você usa a Name
propriedade para controlar o nome ao qual esse tipo será associado no Objective-C mundo. Normalmente, isso é usado para dar ao tipo C# um nome compatível com as Diretrizes de Design do .NET Framework, mas que mapeia para um nome Objective-C que não segue essa convenção.
Por exemplo, no caso a seguir, mapeamos o Objective-CNSURLConnection
tipo para NSUrlConnection
, pois as Diretrizes de Design do .NET Framework usam "Url" em vez de "URL":
[BaseType (typeof (NSObject), Name="NSURLConnection")]
interface NSUrlConnection {
}
O nome especificado é usado como o valor para o atributo gerado [Register]
na associação. Se Name
não for especificado, o nome abreviado do tipo será usado como o valor do [Register]
atributo na saída gerada.
BaseType.Events e BaseType.Delegates
Essas propriedades são usadas para conduzir a geração de eventos no estilo C# nas classes geradas. Eles são usados para vincular uma determinada classe com sua Objective-C classe delegada. Você encontrará muitos casos em que uma classe usa uma classe delegada para enviar notificações e eventos. Por exemplo, a BarcodeScanner
teria uma classe complementar BardodeScannerDelegate
. A BarcodeScanner
classe normalmente teria uma Delegate
propriedade à BarcodeScannerDelegate
qual você atribuiria uma instância, enquanto isso funciona, talvez você queira expor aos usuários uma interface de evento de estilo semelhante a C# e, nesses casos, você usaria as Events
propriedades and Delegates
do [BaseType]
atributo.
Essas propriedades são sempre definidas juntas e devem ter o mesmo número de elementos e ser mantidas em sincronia. A Delegates
matriz contém uma cadeia de caracteres para cada delegado de tipo fraco que você deseja encapsular e a Events
matriz contém um tipo para cada tipo que você deseja associar a ela.
[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 você aplicar esse atributo quando novas instâncias dessa classe forem criadas, a instância desse objeto será mantida até que o método referenciado KeepRefUntil
pelo seja invocado. Isso é útil para melhorar a usabilidade de suas APIs, quando você não deseja que seu usuário mantenha uma referência a um objeto por perto para usar seu código. O valor dessa propriedade é o nome de um método na Delegate
classe, portanto, você também deve usá-lo em combinação com as Events
propriedades e Delegates
.
O exemplo a seguir mostra como isso é usado no UIActionSheet
Xamarin.iOS:
[BaseType (typeof (NSObject), KeepRefUntil="Dismissed")]
[BaseType (typeof (UIView),
KeepRefUntil="Dismissed",
Delegates=new string [] { "WeakDelegate" },
Events=new Type [] {typeof(UIActionSheetDelegate)})]
public interface UIActionSheet {
}
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UIActionSheetDelegate {
[Export ("actionSheet:didDismissWithButtonIndex:"), EventArgs ("UIButton")]
void Dismissed (UIActionSheet actionSheet, nint buttonIndex);
}
DesignatedDefaultCtorAttribute
Quando esse atributo é aplicado à definição de interface, ele gera um [DesignatedInitializer]
atributo no construtor padrão (gerado), que é mapeado para o init
seletor.
DisableDefaultCtorAttribute
Quando esse atributo é aplicado à definição da interface, ele impedirá que o gerador produza o construtor padrão.
Use esse atributo quando precisar que o objeto seja inicializado com um dos outros construtores da classe.
PrivateDefaultCtorAttribute
Quando esse atributo for aplicado à definição de interface, ele sinalizará o construtor padrão como privado. Isso significa que você ainda pode instanciar o objeto dessa classe internamente a partir do arquivo de extensão, mas ele simplesmente não estará acessível aos usuários da sua classe.
Atributo de categoria
Use esse atributo em uma definição de tipo para associar Objective-C categorias e expô-las como métodos de extensão C# para espelhar a maneira como Objective-C expõe a funcionalidade.
As categorias são um Objective-C mecanismo usado para estender o conjunto de métodos e propriedades disponíveis em uma classe. Na prática, eles são usados para estender a funcionalidade de uma classe base (por exemplo NSObject
) quando uma estrutura específica está vinculada (por exemplo UIKit
), disponibilizando seus métodos, mas somente se a nova estrutura estiver vinculada. Em alguns outros casos, eles são usados para organizar recursos em uma classe por funcionalidade. Eles são semelhantes em espírito aos métodos de extensão C#.
É assim que uma categoria ficaria em Objective-C:
@interface UIView (MyUIViewExtension)
-(void) makeBackgroundRed;
@end
O exemplo acima é encontrado em uma biblioteca que estenderia instâncias de UIView
com o método makeBackgroundRed
.
Para associá-los, você pode usar o [Category]
atributo em uma definição de interface. Ao usar o [Category]
atributo, o [BaseType]
significado do atributo muda de ser usado para especificar a classe base a ser estendida para ser o tipo a ser estendido.
O seguinte mostra como as UIView
extensões são associadas e transformadas em métodos de extensão C#:
[BaseType (typeof (UIView))]
[Category]
interface MyUIViewExtension {
[Export ("makeBackgroundRed")]
void MakeBackgroundRed ();
}
O acima criará uma MyUIViewExtension
classe que contém o MakeBackgroundRed
método de extensão. Isso significa que agora você pode chamar MakeBackgroundRed
qualquer UIView
subclasse, oferecendo a mesma funcionalidade que obteria no Objective-C.
Em alguns casos, você encontrará membros estáticos dentro de categorias, como no exemplo a seguir:
@interface FooObject (MyFooObjectExtension)
+ (BOOL)boolMethod:(NSRange *)range;
@end
Isso levará a uma definição incorreta da interface C# da categoria:
[Category]
[BaseType (typeof (FooObject))]
interface FooObject_Extensions {
// Incorrect Interface definition
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
Isso está incorreto porque, para usar a extensão, BoolMethod
você precisa de FooObject
uma instância, mas está associando uma extensão estática ObjC, esse é um efeito colateral devido ao fato de como os métodos de extensão C# são implementados.
A única maneira de usar as definições acima é pelo seguinte código feio:
(null as FooObject).BoolMethod (range);
A recomendação para evitar isso é embutir a BoolMethod
definição dentro da própria definição da FooObject
interface, isso permitirá que você chame essa extensão como se destina FooObject.BoolMethod (range)
.
[BaseType (typeof (NSObject))]
interface FooObject {
[Static]
[Export ("boolMethod:")]
bool BoolMethod (NSRange range);
}
Emitiremos um aviso (BI1117) sempre que encontrarmos um [Static]
membro dentro de uma [Category]
definição. Se você realmente quiser ter [Static]
membros dentro de suas [Category]
definições, poderá silenciar o aviso usando [Category (allowStaticMembers: true)]
ou decorando sua definição de membro ou [Category]
interface com [Internal]
.
Atributo estático
Quando esse atributo é aplicado a uma classe, ele apenas gera uma classe estática, que não deriva de NSObject
, portanto, o [BaseType]
atributo é ignorado. As classes estáticas são usadas para hospedar variáveis públicas C que você deseja expor.
Por exemplo:
[Static]
interface CBAdvertisement {
[Field ("CBAdvertisementDataServiceUUIDsKey")]
NSString DataServiceUUIDsKey { get; }
Gerará uma classe C# com a seguinte API:
public partial class CBAdvertisement {
public static NSString DataServiceUUIDsKey { get; }
}
Definições de protocolo/modelo
Os modelos são normalmente usados pela implementação do protocolo. Eles diferem porque o tempo de execução só será registrado com Objective-C os métodos que realmente foram substituídos. Caso contrário, o método não será registrado.
Em geral, isso significa que, ao criar uma subclasse de uma classe que foi sinalizada com o ModelAttribute
, você não deve chamar o método base. Chamar esse método gerará a seguinte exceção: Foundation.You_Should_Not_Call_base_In_This_Method. Você deve implementar todo o comportamento em sua subclasse para todos os métodos que você substituir.
Atributo abstrato
Por padrão, os membros que fazem parte de um protocolo não são obrigatórios. Isso permite que os usuários criem uma subclasse do Model
objeto simplesmente derivando da classe em C# e substituindo apenas os métodos com os quais eles se importam. Às vezes, o Objective-C contrato exige que o usuário forneça uma implementação para esse método (eles são sinalizados com a @required
diretiva em Objective-C). Nesses casos, você deve sinalizar esses métodos com o [Abstract]
atributo.
O [Abstract]
atributo pode ser aplicado a métodos ou propriedades e faz com que o gerador sinalize o membro gerado como abstrato e a classe como uma classe abstrata.
O seguinte é obtido do Xamarin.iOS:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface UITableViewDataSource {
[Export ("tableView:numberOfRowsInSection:")]
[Abstract]
nint RowsInSection (UITableView tableView, nint section);
}
DefaultValueAttribute
Especifica o valor padrão a ser retornado por um método de modelo se o usuário não fornecer um método para esse método específico no objeto Model
Sintaxe:
public class DefaultValueAttribute : Attribute {
public DefaultValueAttribute (object o);
public object Default { get; set; }
}
Por exemplo, na seguinte classe delegada imaginária para uma Camera
classe, fornecemos um ShouldUploadToServer
que seria exposto como uma propriedade na Camera
classe. Se o usuário da Camera
classe não definir explicitamente um valor para um lambda que possa responder true ou false, o valor padrão retornado neste caso seria false, o valor que especificamos no DefaultValue
atributo:
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("camera:shouldPromptForAction:"), DefaultValue (false)]
bool ShouldUploadToServer (Camera camera, CameraAction action);
}
Se o usuário definir um manipulador na classe imaginária, esse valor será ignorado:
var camera = new Camera ();
camera.ShouldUploadToServer = (camera, action) => return SomeDecision ();
Veja também: [NoDefaultValue]
, [DefaultValueFromArgument]
.
DefaultValueFromArgumentAttribute
Sintaxe:
public class DefaultValueFromArgumentAttribute : Attribute {
public DefaultValueFromArgumentAttribute (string argument);
public string Argument { get; }
}
Esse atributo, quando fornecido em um método que retorna um valor em uma classe de modelo, instruirá o gerador a retornar o valor do parâmetro especificado se o usuário não tiver fornecido seu próprio método ou lambda.
Exemplo:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, nfloat progress);
}
No caso acima, se o usuário da NSAnimation
classe optar por usar qualquer um dos eventos/propriedades do C# e não tiver definido NSAnimation.ComputeAnimationCurve
como um método ou lambda, o valor retornado será o valor passado no parâmetro progress.
Veja também: [NoDefaultValue]
, [DefaultValue]
IgnoredInDelegateAttribute
Às vezes, faz sentido não expor um evento ou delegar propriedade de uma classe Model na classe host, portanto, adicionar esse atributo instruirá o gerador a evitar a geração de qualquer método decorado com ele.
[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
Esse atributo é usado em métodos Model que retornam valores para definir o nome da assinatura delegada a ser usada.
Exemplo:
[BaseType (typeof (NSObject))]
[Model][Protocol]
public interface NSAnimationDelegate {
[Export ("animation:valueForProgress:"), DelegateName ("NSAnimationProgress"), DefaultValueFromArgumentAttribute ("progress")]
float ComputeAnimationCurve (NSAnimation animation, float progress);
}
Com a definição acima, o gerador produzirá a seguinte declaração pública:
public delegate float NSAnimationProgress (MonoMac.AppKit.NSAnimation animation, float progress);
DelegateApiNameAttribute
Esse atributo é usado para permitir que o gerador altere o nome da propriedade gerada na classe de host. Às vezes, é útil quando o nome do método da classe FooDelegate faz sentido para a classe Delegate, mas pareceria estranho na classe host como uma propriedade.
Além disso, isso é realmente útil (e necessário) quando você tem dois ou mais métodos de sobrecarga que fazem sentido mantê-los nomeados como estão na classe FooDelegate, mas você deseja expô-los na classe host com um nome melhor.
Exemplo:
[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);
}
Com a definição acima, o gerador produzirá a seguinte declaração pública na classe host:
public Func<NSAnimation, float, float> ComputeAnimationCurve { get; set; }
Atributo EventArgs
Para eventos que usam mais de um parâmetro (na Objective-C convenção é que o primeiro parâmetro em uma classe delegada é a instância do objeto remetente), você deve fornecer o nome que deseja que a classe EventArgs gerada seja. Isso é feito com o [EventArgs]
atributo na declaração do método em sua Model
classe.
Por exemplo:
[BaseType (typeof (UINavigationControllerDelegate))]
[Model][Protocol]
public interface UIImagePickerControllerDelegate {
[Export ("imagePickerController:didFinishPickingImage:editingInfo:"), EventArgs ("UIImagePickerImagePicked")]
void FinishedPickingImage (UIImagePickerController picker, UIImage image, NSDictionary editingInfo);
}
A declaração acima gerará uma UIImagePickerImagePickedEventArgs
classe que deriva de EventArgs e empacota ambos os parâmetros, o e o NSDictionary
UIImage
. O gerador produz isso:
public partial class UIImagePickerImagePickedEventArgs : EventArgs {
public UIImagePickerImagePickedEventArgs (UIImage image, NSDictionary editingInfo);
public UIImage Image { get; set; }
public NSDictionary EditingInfo { get; set; }
}
Em seguida, ele expõe o UIImagePickerController
seguinte na classe:
public event EventHandler<UIImagePickerImagePickedEventArgs> FinishedPickingImage { add; remove; }
Atributo de nome do evento
Esse atributo é usado para permitir que o gerador altere o nome de um evento ou propriedade gerada na classe. Às vezes, é útil quando o nome do método da classe Model faz sentido para a classe model, mas pareceria estranho na classe de origem como um evento ou propriedade.
Por exemplo, o UIWebView
usa o seguinte bit do UIWebViewDelegate
:
[Export ("webViewDidFinishLoad:"), EventArgs ("UIWebView"), EventName ("LoadFinished")]
void LoadingFinished (UIWebView webView);
O acima expõe LoadingFinished
como o método no UIWebViewDelegate
, mas LoadFinished
como o evento para conectar em um UIWebView
:
var webView = new UIWebView (...);
webView.LoadFinished += delegate { Console.WriteLine ("done!"); }
Atributo do modelo
Quando você aplica o [Model]
atributo a uma definição de tipo em sua API de contrato, o runtime gera um código especial que só exibirá invocações para métodos na classe se o usuário tiver substituído um método na classe. Esse atributo normalmente é aplicado a todas as APIs que encapsulam uma Objective-C classe delegada.
O runtime também gerará uma Objective-C classe que corresponde ao nome do protocolo correspondente.
É possível personalizar o nome da Objective-C classe de duas maneiras:
Configuração
AutoGeneratedName = true
:[Model (AutoGeneratedName = true)]
Isso fará com que o runtime gere um nome exclusivo para o Objective-C tipo. Atualmente, o nome é baseado no nome da montagem e no nome completo do tipo do modelo (isso pode mudar no futuro).
Especificando explicitamente o nome:
[Model (Name = "CustomName")]
Recomenda-se usar AutoGeneratedName = true
o . No .NET, o nome é sempre gerado (a menos que seja explicitamente especificado como em 2. acima) e a AutoGeneratedName
propriedade não existe mais.
NoDefaultValueAttribute
Especifica que o método no modelo não fornece um valor retornado padrão.
Isso funciona com o Objective-C runtime respondendo false
à solicitação de Objective-C runtime para determinar se o seletor especificado está implementado nessa classe.
[BaseType (typeof (NSObject))]
[Model][Protocol]
interface CameraDelegate {
[Export ("shouldDisplayPopup"), NoDefaultValue]
bool ShouldUploadToServer ();
}
Veja também: [DefaultValue]
, [DefaultValueFromArgument]
Protocolos
O Objective-C conceito de protocolo realmente não existe em C#. Os protocolos são semelhantes às interfaces C#, mas diferem porque nem todos os métodos e propriedades declarados em um protocolo devem ser implementados pela classe que o adota. Em vez disso, alguns dos métodos e propriedades são opcionais.
Alguns protocolos são geralmente usados como classes Model, que devem ser vinculados usando o [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 do Xamarin.iOS 7.0, uma funcionalidade de associação de protocolo nova e aprimorada foi incorporada. Qualquer definição que contenha o atributo gerará, na verdade, [Protocol]
três classes de suporte que melhoram muito a maneira como você consome 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);
}
}
A implementação da classe fornece uma classe abstrata completa da qual você pode substituir métodos individuais e obter segurança de tipo completa. Mas, como o C# não dá suporte a várias heranças, há cenários em que você pode exigir uma classe base diferente, mas ainda deseja implementar uma interface.
É aqui que entra a definição de interface gerada. É uma interface que possui todos os métodos necessários do protocolo. Isso permite que os desenvolvedores que desejam implementar seu protocolo simplesmente implementem a interface. O runtime registrará automaticamente o tipo como adotando o protocolo.
Observe que a interface lista apenas os métodos necessários e expõe os métodos opcionais. Isso significa que as classes que adotam o protocolo obterão verificação completa de tipo para os métodos necessários, mas terão que recorrer à tipagem fraca (usando manualmente atributos Export e correspondendo à assinatura) para os métodos de protocolo opcionais.
Para facilitar o consumo de uma API que usa protocolos, a ferramenta de associação também produzirá uma classe de método de extensões que expõe todos os métodos opcionais. Isso significa que, enquanto você estiver consumindo uma API, poderá tratar os protocolos como tendo todos os métodos.
Se você quiser usar as definições de protocolo em sua API, precisará escrever interfaces vazias de esqueleto em sua definição de API. Se você quiser usar o MyProtocol em uma API, precisará fazer o seguinte:
[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 ();
}
O acima é necessário porque no momento da ligação o IMyProtocol
não existiria, é por isso que você precisa fornecer uma interface vazia.
Adotando interfaces geradas por protocolo
Sempre que você implementar uma das interfaces geradas para os protocolos, assim:
class MyDelegate : NSObject, IUITableViewDelegate {
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
A implementação dos métodos de interface necessários é exportada com o nome adequado, portanto, é equivalente a isso:
class MyDelegate : NSObject, IUITableViewDelegate {
[Export ("getRowHeight:")]
nint IUITableViewDelegate.GetRowHeight (nint row) {
return 1;
}
}
Isso funcionará para todos os membros de protocolo necessários, mas há um caso especial com seletores opcionais a serem considerados.
Os membros opcionais do protocolo são tratados de forma idêntica ao usar a classe base:
public class UrlSessionDelegate : NSUrlSessionDownloadDelegate {
public override void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
mas ao usar a interface do protocolo, é necessário adicionar o [Export]. O IDE irá adicioná-lo por meio do preenchimento automático quando você adicioná-lo começando com a substituição.
public class UrlSessionDelegate : NSObject, INSUrlSessionDownloadDelegate {
[Export ("URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:")]
public void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
Há uma pequena diferença de comportamento entre os dois em tempo de execução:
- Os usuários da classe base (NSUrlSessionDownloadDelegate no exemplo) fornecem todos os seletores obrigatórios e opcionais, retornando valores padrão razoáveis.
- Os usuários da interface (INSUrlSessionDownloadDelegate no exemplo) respondem apenas aos seletores exatos fornecidos.
Algumas classes raras podem se comportar de maneira diferente aqui. Em quase todos os casos, no entanto, é seguro usar qualquer um.
Inlining de protocolo
Ao associar tipos existentes Objective-C que foram declarados como adotando um protocolo, você desejará embutir o protocolo diretamente. Para fazer isso, basta declarar seu protocolo como uma interface sem nenhum [BaseType]
atributo e listar o protocolo na lista de interfaces base para sua interface.
Exemplo:
interface SpeakProtocol {
[Export ("say:")]
void Say (string msg);
}
[BaseType (typeof (NSObject))]
interface Robot : SpeakProtocol {
[Export ("awake")]
bool Awake { get; set; }
}
Definições de membro
Os atributos nesta seção são aplicados a membros individuais de um tipo: propriedades e declarações de método.
AlignAttribute
Usado para especificar o valor de alinhamento para tipos de retorno de propriedade. Determinadas propriedades usam ponteiros para endereços que devem ser alinhados em determinados limites (no Xamarin.iOS, isso acontece, por exemplo, com algumas GLKBaseEffect
propriedades que devem ser alinhadas a 16 bytes). Você pode usar essa propriedade para decorar o getter e usar o valor de alinhamento. Isso normalmente é usado com os OpenTK.Vector4
tipos e OpenTK.Matrix4
quando integrado a Objective-C APIs.
Exemplo:
public interface GLKBaseEffect {
[Export ("constantColor")]
Vector4 ConstantColor { [Align (16)] get; set; }
}
Atributo de aparência
O [Appearance]
atributo é limitado ao iOS 5, onde o gerenciador de aparência foi introduzido.
O [Appearance]
atributo pode ser aplicado a qualquer método ou propriedade que participe da UIAppearance
estrutura. Quando esse atributo é aplicado a um método ou propriedade em uma classe, ele direciona o gerador de associação para criar uma classe de aparência fortemente tipada que é usada para estilizar todas as instâncias dessa classe ou as instâncias que correspondem a determinados critérios.
Exemplo:
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);
}
O acima geraria o seguinte código em 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)
Use os [AutoReleaseAttribute]
métodos e propriedades on para encapsular a invocação do método para o método em um NSAutoReleasePool
.
Existem Objective-C alguns métodos que retornam valores que são adicionados ao padrão NSAutoReleasePool
. Por padrão, eles iriam para o seu thread NSAutoReleasePool
, mas como o Xamarin.iOS também mantém uma referência aos seus objetos enquanto o objeto gerenciado estiver vivo, talvez você não queira manter uma referência extra no que só será drenada NSAutoReleasePool
até que seu thread retorne o controle para o próximo thread ou você volte para o loop principal.
Esse atributo é aplicado, por exemplo, em propriedades pesadas (por exemplo, UIImage.FromFile
) que retorna objetos que foram adicionados ao padrão NSAutoReleasePool
. Sem esse atributo, as imagens seriam retidas desde que seu thread não retornasse o controle para o loop principal. Uf seu tópico era algum tipo de downloader de fundo que está sempre vivo e esperando pelo trabalho, as imagens nunca seriam liberadas.
ForcedTypeAttribute
O [ForcedTypeAttribute]
é usado para impor a criação de um tipo gerenciado, mesmo que o objeto não gerenciado retornado não corresponda ao tipo descrito na definição de associação.
Isso é útil quando o tipo descrito em um cabeçalho não corresponde ao tipo retornado do método nativo, por exemplo, pegue a seguinte Objective-C definição de NSURLSession
:
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
Ele afirma claramente que retornará uma NSURLSessionDownloadTask
instância, mas ainda assim retorna um NSURLSessionTask
, que é uma superclasse e, portanto, não pode ser convertível em NSURLSessionDownloadTask
. Como estamos em um contexto de tipo seguro, isso InvalidCastException
acontecerá.
Para cumprir a descrição do cabeçalho e evitar o InvalidCastException
, o [ForcedTypeAttribute]
é usado.
[BaseType (typeof (NSObject), Name="NSURLSession")]
interface NSUrlSession {
[Export ("downloadTaskWithRequest:")]
[return: ForcedType]
NSUrlSessionDownloadTask CreateDownloadTask (NSUrlRequest request);
}
O [ForcedTypeAttribute]
também aceita um valor booleano chamado Owns
que é false
por padrão [ForcedType (owns: true)]
. O parâmetro owns é usado para seguir a Política de Propriedade para objetos do Core Foundation .
O [ForcedTypeAttribute]
é válido apenas em parâmetros, propriedades e valor retornado.
BindAsAttribute
O [BindAsAttribute]
permite a associação NSNumber
e NSString
NSValue
(enumerações) em tipos C# mais precisos. O atributo pode ser usado para criar uma API .NET melhor e mais precisa sobre a API nativa.
Você pode decorar métodos (no valor retornado), parâmetros e propriedades com BindAs
. A única restrição é que seu membro não deve estar dentro de uma [Protocol]
interface or [Model]
.
Por exemplo:
[return: BindAs (typeof (bool?))]
[Export ("shouldDrawAt:")]
NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect);
Produziria:
[Export ("shouldDrawAt:")]
bool? ShouldDraw (CGRect rect) { ... }
Internamente, faremos as bool?
conversões ->NSNumber
e CGRect
<->NSValue
.<
Os tipos de encapsulamento com suporte atuais são:
NSValue
NSNumber
NSString
NSValue
Os seguintes tipos de dados C# têm suporte para serem encapsulados de/para NSValue
:
- CGAffineTransform
- NSRange
- CGVector
- SCNMatrix4
- CLLocationCoordinate2D
- SCNVector3
- SCNVector4
- CGPoint / PontoF
- CGRect / RetânguloF
- CGSize / SizeF
- Inserções de interface do usuário
- UIOffset
- MKCoordinateSpan
- CMTimeRange
- CMTime
- Mapeamento CMTime
- CATransform3D
Número NSNúmber
Os seguintes tipos de dados C# têm suporte para serem encapsulados de/para NSNumber
:
- bool
- byte
- duplo
- float
- short
- INT
- longo
- sbyte
- ushort
- uint
- ulong
- nfloat
- nint
- nuint
- Enumerações
NSString
[BindAs]
funciona em conjunto com enumerações apoiadas por uma constante NSString para que você possa criar uma API .NET melhor, por exemplo:
[BindAs (typeof (CAScroll))]
[Export ("supportedScrollMode")]
NSString SupportedScrollMode { get; set; }
Produziria:
[Export ("supportedScrollMode")]
CAScroll SupportedScrollMode { get; set; }
Lidaremos com a enum
<conversão -NSString
> somente se o tipo de enumeração fornecido for [BindAs]
apoiado por uma constante NSString .
matrizes
[BindAs]
também suporta arrays de qualquer um dos tipos suportados, você pode ter a seguinte definição de API como exemplo:
[return: BindAs (typeof (CAScroll []))]
[Export ("getScrollModesAt:")]
NSString [] GetScrollModes ([BindAs (typeof (CGRect []))] NSValue [] rects);
Produziria:
[Export ("getScrollModesAt:")]
CAScroll? [] GetScrollModes (CGRect [] rects) { ... }
O rects
parâmetro será encapsulado em um NSArray
que contém um NSValue
para each CGRect
e, em troca, você obterá uma matriz CAScroll?
que foi criada usando os valores do .NSArray
NSStrings
Atributo Bind
O [Bind]
atributo tem dois usos, um quando aplicado a uma declaração de método ou propriedade e outro quando aplicado ao getter ou setter individual em uma propriedade.
Quando usado para um método ou propriedade, o [Bind]
efeito do atributo é gerar um método que invoca o seletor especificado. Mas o método gerado resultante não é decorado com o atributo, o [Export]
que significa que ele não pode participar da substituição do método. Isso normalmente é usado em combinação com o [Target]
atributo para implementar Objective-C métodos de extensão.
Por exemplo:
public interface UIView {
[Bind ("drawAtPoint:withFont:")]
SizeF DrawString ([Target] string str, CGPoint point, UIFont font);
}
Quando usado em um getter ou setter, o [Bind]
atributo é usado para alterar os padrões inferidos pelo gerador de código ao gerar os nomes do seletor getter e setter Objective-C para uma propriedade. Por padrão, quando você sinaliza uma propriedade com o nome fooBar
, o gerador geraria uma fooBar
exportação para o getter e setFooBar:
para o setter. Em alguns casos, Objective-C não segue essa convenção, geralmente eles mudam o nome do getter para ser isFooBar
.
Você usaria esse atributo para informar o gerador sobre isso.
Por exemplo:
// Default behavior
[Export ("active")]
bool Active { get; set; }
// Custom naming with the Bind attribute
[Export ("visible")]
bool Visible { [Bind ("isVisible")] get; set; }
AsyncAttribute
Disponível apenas no Xamarin.iOS 6.3 e mais recente.
Esse atributo pode ser aplicado a métodos que usam um manipulador de conclusão como seu último argumento.
Você pode usar o [Async]
atributo em métodos cujo último argumento é um retorno de chamada. Quando você aplica isso a um método, o gerador de associação gerará uma versão desse método com o sufixo Async
. Se o retorno de chamada não usar parâmetros, o valor retornado será um Task
, se o retorno de chamada usar um parâmetro, o resultado será um Task<T>
.
[Export ("upload:complete:")]
[Async]
void LoadFile (string file, NSAction complete)
O seguinte gerará este método assíncrono:
Task LoadFileAsync (string file);
Se o retorno de chamada usar vários parâmetros, você deverá definir o ResultType
ou ResultTypeName
para especificar o nome desejado do tipo gerado que conterá todas as propriedades.
delegate void OnComplete (string [] files, nint byteCount);
[Export ("upload:complete:")]
[Async (ResultTypeName="FileLoading")]
void LoadFiles (string file, OnComplete complete)
O seguinte irá gerar este método assíncrono, onde FileLoading
contém propriedades para acessar ambos files
e byteCount
:
Task<FileLoading> LoadFile (string file);
Se o último parâmetro do retorno de chamada for um NSError
, o método gerado Async
verificará se o valor não é nulo e, se for esse o caso, o método assíncrono gerado definirá a exceção da tarefa.
[Export ("upload:onComplete:")]
[Async]
void Upload (string file, Action<string,NSError> onComplete);
O acima gera o seguinte método assíncrono:
Task<string> UploadAsync (string file);
E em caso de erro, a Task resultante terá a exceção definida como um NSErrorException
que envolve o .NSError
AsyncAttribute.ResultType
Use essa propriedade para especificar o valor do objeto de retorno Task
. Esse parâmetro usa um tipo existente, portanto, ele precisa ser definido em uma das definições de API principais.
AsyncAttribute.ResultTypeName
Use essa propriedade para especificar o valor do objeto de retorno Task
. Este parâmetro recebe o nome do nome do tipo desejado, o gerador produzirá uma série de propriedades, uma para cada parâmetro que o retorno de chamada obtém.
AsyncAttribute.MethodName
Use essa propriedade para personalizar o nome dos métodos assíncronos gerados. O padrão é usar o nome do método e anexar o texto "Async", você pode usar isso para alterar esse padrão.
DesignatedInitializerAttribute
Quando esse atributo é aplicado a um construtor, ele gera o mesmo [DesignatedInitializer]
no assembly final da plataforma. Isso é para ajudar o IDE a indicar qual construtor deve ser usado em subclasses.
Isso deve mapear para Objective-Co uso de /clang de __attribute__((objc_designated_initializer))
.
DisableZeroCopyAttribute
Esse atributo é aplicado a parâmetros de cadeia de caracteres ou propriedades de cadeia de caracteres e instrui o gerador de código a não usar o marshaling de cadeia de caracteres de cópia zero para esse parâmetro e, em vez disso, criar uma nova instância NSString da cadeia de caracteres C#.
Esse atributo só será necessário em cadeias de caracteres se você instruir o gerador a usar o marshaling de cadeia de caracteres de cópia zero usando a opção de --zero-copy
linha de comando ou definindo o atributo ZeroCopyStringsAttribute
de nível de assembly.
Isso é necessário nos casos em que a propriedade é declarada como Objective-C uma retain
propriedade ou assign
em vez de uma copy
propriedade. Isso geralmente acontece em bibliotecas de terceiros que foram "otimizadas" erroneamente pelos desenvolvedores. Em geral, as propriedades or assign
NSString
estão incorretas, retain
pois NSMutableString
as classes derivadas do usuário podem NSString
alterar o conteúdo das cadeias de caracteres sem o conhecimento do código da biblioteca, quebrando sutilmente o aplicativo. Normalmente, isso acontece devido à otimização prematura.
O seguinte mostra duas dessas propriedades em Objective-C:
@property(nonatomic,retain) NSString *name;
@property(nonatomic,assign) NSString *name2;
Atributo de descarte
Ao aplicar o [DisposeAttribute]
a uma classe, você fornece um snippet de código que será adicionado à implementação do Dispose()
método da classe.
Como o Dispose
método é gerado automaticamente pela bgen
ferramenta, você precisa usar o [Dispose]
atributo para injetar algum código na implementação do método gerado Dispose
.
Por exemplo:
[BaseType (typeof (NSObject))]
[Dispose ("if (OpenConnections > 0) CloseAllConnections ();")]
interface DatabaseConnection {
}
ExportAttribute
O [Export]
atributo é usado para sinalizar um método ou propriedade a ser exposto ao Objective-C runtime. Esse atributo é compartilhado entre a ferramenta de associação e os runtimes reais do Xamarin.iOS e do Xamarin.Mac. Para métodos, o parâmetro é passado literalmente para o código gerado, para propriedades, um getter e setter As exportações são geradas com base na declaração base (consulte a seção sobre o [BindAttribute]
para obter informações sobre como alterar o comportamento da ferramenta de associação).
Sintaxe:
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; }
}
O seletor representa o nome do método ou propriedade subjacente Objective-C que está sendo associado.
ExportAttribute.ArgumentSemantic
Atributo de campo
Esse atributo é usado para expor uma variável global C como um campo que é carregado sob demanda e exposto ao código C#. Normalmente, isso é necessário para obter os valores de constantes definidas em C ou Objective-C e que podem ser tokens usados em algumas APIs ou cujos valores são opacos e devem ser usados no estado em que se encontram pelo código do usuário.
Sintaxe:
public class FieldAttribute : Attribute {
public FieldAttribute (string symbolName);
public FieldAttribute (string symbolName, string libraryName);
public string SymbolName { get; set; }
public string LibraryName { get; set; }
}
O symbolName
é o símbolo C com o qual se vincular. Por padrão, isso será carregado de uma biblioteca cujo nome é inferido do namespace em que o tipo é definido. Se esta não for a biblioteca onde o símbolo é pesquisado, você deve passar o libraryName
parâmetro. Se você estiver vinculando uma biblioteca estática, use __Internal
como parâmetro libraryName
.
As propriedades geradas são sempre estáticas.
As propriedades sinalizadas com o atributo Field podem ser dos seguintes tipos:
NSString
NSArray
nint
/int
/long
nuint
/uint
/ulong
nfloat
/float
double
CGSize
System.IntPtr
- Enumerações
Setters não têm suporte para enumerações apoiadas por constantes NSString, mas podem ser associados manualmente, se necessário.
Exemplo:
[Static]
interface CameraEffects {
[Field ("kCameraEffectsZoomFactorKey", "CameraLibrary")]
NSString ZoomFactorKey { get; }
}
Atributo Interno
O [Internal]
atributo pode ser aplicado a métodos ou propriedades e tem o efeito de sinalizar o código gerado com a internal
palavra-chave C#, tornando o código acessível apenas ao código no assembly gerado. Isso normalmente é usado para ocultar APIs de nível muito baixo ou fornecer uma API pública abaixo do ideal que você deseja melhorar ou para APIs que não são suportadas pelo gerador e exigem alguma codificação manual.
Ao criar a associação, você normalmente ocultaria o método ou a propriedade usando esse atributo e forneceria um nome diferente para o método ou a propriedade e, em seguida, no arquivo de suporte complementar do C#, adicionaria um wrapper fortemente tipado que expõe a funcionalidade subjacente.
Por exemplo:
[Internal]
[Export ("setValue:forKey:")]
void _SetValueForKey (NSObject value, NSObject key);
[Internal]
[Export ("getValueForKey:")]
NSObject _GetValueForKey (NSObject key);
Então, em seu arquivo de suporte, você pode ter algum código como este:
public NSObject this [NSObject idx] {
get {
return _GetValueForKey (idx);
}
set {
_SetValueForKey (value, idx);
}
}
IsThreadStaticAttribute
Esse atributo sinaliza o campo de suporte de uma propriedade a ser anotada com o atributo .NET [ThreadStatic]
. Isso será útil se o campo for uma variável estática de thread.
MarshalNativeExceptions (Xamarin.iOS 6.0.6)
Esse atributo fará com que um método suporte exceções nativas (Objective-C).
Em vez de chamar objc_msgSend
diretamente, a invocação passará por um trampolim personalizado que captura exceções ObjectiveC e as empacota em exceções gerenciadas.
Atualmente, apenas algumas objc_msgSend
assinaturas são suportadas (você descobrirá se uma assinatura não é suportada quando a vinculação nativa de um aplicativo que usa a associação falhar com um símbolo _objc_msgSend xamarin_ ausente), mas mais podem ser adicionadas mediante solicitação.
Novo Atributo
Esse atributo é aplicado a métodos e propriedades para que o gerador gere a new
palavra-chave na frente da declaração.
Ele é usado para evitar avisos do compilador quando o mesmo método ou nome de propriedade é introduzido em uma subclasse que já existia em uma classe base.
Atributo de Notificação
Você pode aplicar esse atributo a campos para que o gerador produza uma classe Notifications auxiliar fortemente tipada.
Esse atributo pode ser usado sem argumentos para notificações que não carregam carga ou você pode especificar um System.Type
que faz referência a outra interface na definição da API, normalmente com o nome terminando com "EventArgs". O gerador transformará a interface em uma classe que subclasse EventArgs
e incluirá todas as propriedades listadas lá. O [Export]
atributo deve ser usado na EventArgs
classe para listar o nome da chave usada para pesquisar o Objective-C dicionário para buscar o valor.
Por exemplo:
interface MyClass {
[Notification]
[Field ("MyClassDidStartNotification")]
NSString DidStartNotification { get; }
}
O código acima gerará uma classe MyClass.Notifications
aninhada com os seguintes métodos:
public class MyClass {
[..]
public Notifications {
public static NSObject ObserveDidStart (EventHandler<NSNotificationEventArgs> handler)
public static NSObject ObserveDidStart (NSObject objectToObserve, EventHandler<NSNotificationEventArgs> handler)
}
}
Os usuários do seu código podem assinar facilmente as notificações postadas no NSDefaultCenter usando um código como este:
var token = MyClass.Notifications.ObserverDidStart ((notification) => {
Console.WriteLine ("Observed the 'DidStart' event!");
});
Ou para definir um objeto específico para observar. Se você passar null
para objectToObserve
este método vai se comportar exatamente como o seu outro par.
var token = MyClass.Notifications.ObserverDidStart (objectToObserve, (notification) => {
Console.WriteLine ("Observed the 'DidStart' event on objectToObserve!");
});
O valor retornado de ObserveDidStart
pode ser usado para parar facilmente de receber notificações, assim:
token.Dispose ();
Ou você pode chamar NSNotification.DefaultCenter.RemoveObserver e passar o token. Se sua notificação contiver parâmetros, você deverá especificar uma interface auxiliar EventArgs
, como esta:
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; }
}
O acima gerará uma MyScreenChangedEventArgs
classe com as ScreenX
propriedades and ScreenY
que buscarão os dados do dicionário NSNotification.UserInfo usando as chaves ScreenXKey
e ScreenYKey
respectivamente e aplicarão as conversões adequadas. O [ProbePresence]
atributo é usado para o gerador investigar se a chave está definida no UserInfo
, em vez de tentar extrair o valor. Isso é usado para casos em que a presença da chave é o valor (normalmente para valores booleanos).
Isso permite que você escreva um código como este:
var token = MyClass.NotificationsObserveScreenChanged ((notification) => {
Console.WriteLine ("The new screen dimensions are {0},{1}", notification.ScreenX, notification.ScreenY);
});
Em alguns casos, não há nenhuma constante associada ao valor passado no dicionário. A Apple às vezes usa constantes de símbolo público e às vezes usa constantes de string. Por padrão, o [Export]
atributo em sua classe fornecida EventArgs
usará o nome especificado como um símbolo público a ser pesquisado em runtime. Se esse não for o caso e, em vez disso, ele deve ser pesquisado como uma constante de string, passe o ArgumentSemantic.Assign
valor para o atributo Export.
Novidades no Xamarin.iOS 8.4
Às vezes, as notificações começam a vida sem nenhum argumento, portanto, o uso de [Notification]
sem argumentos é aceitável. Mas, às vezes, parâmetros para a notificação serão introduzidos. Para dar suporte a esse cenário, o atributo pode ser aplicado mais de uma vez.
Se você estiver desenvolvendo uma associação e quiser evitar a quebra do código de usuário existente, transforme uma notificação existente de:
interface MyClass {
[Notification]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
Em uma versão que lista o atributo de notificação duas vezes, assim:
interface MyClass {
[Notification]
[Notification (typeof (MyScreenChangedEventArgs)]
[Field ("MyClassScreenChangedNotification")]
NSString ScreenChangedNotification { get; }
}
NullAllowedAttribute
Quando isso é aplicado a uma propriedade, ele sinaliza a propriedade como permitindo que o valor null
seja atribuído a ela. Isso só é válido para tipos de referência.
Quando isso é aplicado a um parâmetro em uma assinatura de método, indica que o parâmetro especificado pode ser nulo e que nenhuma verificação deve ser executada para passar null
valores.
Se o tipo de referência não tiver esse atributo, a ferramenta de vinculação gerará uma verificação para o valor que está sendo atribuído antes de passá-lo e Objective-C gerará uma verificação que lançará um ArgumentNullException
se o valor atribuído for null
.
Por exemplo:
// In properties
[NullAllowed]
UIImage IconFile { get; set; }
// In methods
void SetImage ([NullAllowed] UIImage image, State forState);
Atributo de substituição
Use esse atributo para instruir o gerador de associação que a associação para esse método específico deve ser sinalizada com uma override
palavra-chave.
PreSnippetAttribute
Você pode usar esse atributo para injetar algum código a ser inserido depois que os parâmetros de entrada forem validados, mas antes que o código chame .Objective-C
Exemplo:
[Export ("demo")]
[PreSnippet ("var old = ViewController;")]
void Demo ();
PrólogoSnippetAtributo
Você pode usar esse atributo para injetar algum código a ser inserido antes que qualquer um dos parâmetros seja validado no método gerado.
Exemplo:
[Export ("demo")]
[Prologue ("Trace.Entry ();")]
void Demo ();
PostGetAttribute
Instrui o gerador de associação a invocar a propriedade especificada dessa classe para buscar um valor dela.
Essa propriedade normalmente é usada para atualizar o cache que aponta para objetos de referência que mantêm o grafo de objeto referenciado. Normalmente, ele aparece no código que tem operações como Adicionar/Remover. Esse método é usado para que, depois que os elementos forem adicionados ou removidos, o cache interno seja atualizado para garantir que estamos mantendo referências gerenciadas a objetos que estão realmente em uso. Isso é possível porque a ferramenta de vinculação gera um campo de suporte para todos os objetos de referência em uma determinada vinculação.
Exemplo:
[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; }
}
Nesse caso, a Dependencies
propriedade será invocada após adicionar ou remover dependências do objeto, garantindo que tenhamos um gráfico que represente os objetos carregados reais, evitando vazamentos de memória e corrupção de NSOperation
memória.
PostSnippetAttribute
Você pode usar esse atributo para injetar algum código-fonte C# a ser inserido depois que o código invocar o método subjacente Objective-C
Exemplo:
[Export ("demo")]
[PostSnippet ("if (old != null) old.DemoComplete ();")]
void Demo ();
Atributo Proxy
Esse atributo é aplicado para retornar valores para sinalizá-los como objetos proxy. Algumas Objective-C APIs retornam objetos proxy que não podem ser diferenciados das associações de usuário. O efeito desse atributo é sinalizar o objeto como sendo um DirectBinding
objeto. Para um cenário no Xamarin.Mac, você pode ver a discussão sobre esse bug.
ReleaseAttribute (Xamarin.iOS 6.0)
Isso pode ser aplicado a tipos de retorno para indicar que o gerador deve chamar Release
o objeto antes de retorná-lo. Isso só é necessário quando um método fornece um objeto retido (em oposição a um objeto liberado automaticamente, que é o cenário mais comum)
Exemplo:
[Export ("getAndRetainObject")]
[return: Release ()]
NSObject GetAndRetainObject ();
Além disso, esse atributo é propagado para o código gerado, para que o runtime do Xamarin.iOS saiba que ele deve reter o objeto ao retornar a Objective-C partir de tal função.
Atributo Selado
Instrui o gerador a sinalizar o método gerado como lacrado. Se esse atributo não for especificado, o padrão será gerar um método virtual (um método virtual, um método abstrato ou uma substituição, dependendo de como outros atributos são usados).
Atributo estático
Quando o [Static]
atributo é aplicado a um método ou propriedade, isso gera um método ou propriedade estática. Se esse atributo não for especificado, o gerador produzirá um método ou propriedade de instância.
Atributo transitório
Use esse atributo para sinalizar propriedades cujos valores são transitórios, ou seja, objetos criados temporariamente pelo iOS, mas não têm vida longa. Quando esse atributo é aplicado a uma propriedade, o gerador não cria um campo de suporte para essa propriedade, o que significa que a classe gerenciada não mantém uma referência ao objeto.
WrapAttribute
No design das associações Xamarin.iOS/Xamarin.Mac, o [Wrap]
atributo é usado para encapsular um objeto de tipo fraco com um objeto de tipo forte. Isso entra em jogo principalmente com Objective-C objetos delegados que normalmente são declarados como sendo do tipo id
ou NSObject
. A convenção usada pelo Xamarin.iOS e Xamarin.Mac é expor esses delegados ou fontes de dados como sendo do tipo NSObject
e são nomeados usando a convenção "Weak" + o nome que está sendo exposto. Uma id delegate
propriedade de seria exposta como uma NSObject WeakDelegate { get; set; }
propriedade no arquivo de contrato da Objective-C API.
Mas, normalmente, o valor atribuído a esse delegado é de um tipo forte, portanto, exibimos o tipo forte e aplicamos o [Wrap]
atributo, isso significa que os usuários podem optar por usar tipos fracos se precisarem de algum controle fino ou se precisarem recorrer a truques de baixo nível, ou podem usar a propriedade fortemente tipada para a maior parte de seu trabalho.
Exemplo:
[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 ();
}
É assim que o usuário usaria a versão fracamente tipada do 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 ();
E é assim que o usuário usaria a versão fortemente tipada, observe que o usuário aproveita o sistema de tipos do C# e está usando a palavra-chave override para declarar sua intenção e não precisa decorar manualmente o método com [Export]
, já que fizemos esse trabalho na ligação para o usuário:
// This is the strong case,
class MyDelegate : DemoDelegate {
override void Demo DoDemo () {}
}
var strongDemo = new Demo ();
demo.Delegate = new MyDelegate ();
Outro uso do [Wrap]
atributo é dar suporte a uma versão fortemente tipada de métodos. Por exemplo:
[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 o [Wrap]
atributo é aplicado em um método dentro de um tipo decorado com um [Category]
atributo, você precisa incluir This
como o primeiro argumento, pois um método de extensão está sendo gerado. Por exemplo:
[Wrap ("Write (This, image, options?.Dictionary, out error)")]
bool Write (CIImage image, CIImageRepresentationOptions options, out NSError error);
Os membros gerados por [Wrap]
não virtual
são por padrão, se você precisar de um virtual
membro, poderá definir o true
parâmetro 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]
também pode ser usado diretamente em getters e setters de propriedade.
Isso permite ter controle total sobre eles e ajustar o código conforme necessário.
Por exemplo, considere a seguinte definição de API que usa enumerações inteligentes:
// Smart enum.
enum PersonRelationship {
[Field (null)]
None,
[Field ("FMFather", "__Internal")]
Father,
[Field ("FMMother", "__Internal")]
Mother
}
Definição de interface:
// Property definition.
[Export ("presenceType")]
NSString _PresenceType { get; set; }
PersonRelationship PresenceType {
[Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType)")]
get;
[Wrap ("_PresenceType = value.GetConstant ()")]
set;
}
Atributos de parâmetro
Esta seção descreve os atributos que você pode aplicar aos parâmetros em uma definição de método, bem como o [NullAttribute]
que se aplica a uma propriedade como um todo.
Retorno de chamada de bloco
Esse atributo é aplicado a tipos de parâmetro em declarações de delegado C# para notificar o associador de que o parâmetro em questão está em conformidade com a convenção de chamada de Objective-C bloco e deve empacotá-lo dessa maneira.
Isso normalmente é usado para retornos de chamada definidos assim em Objective-C:
typedef returnType (^SomeTypeDefinition) (int parameter1, NSString *parameter2);
Veja também: CCallback.
CCallback
Esse atributo é aplicado a tipos de parâmetro em declarações de delegado C# para notificar o associador de que o parâmetro em questão está em conformidade com a convenção de chamada de ponteiro de função C ABI e deve empacotá-lo dessa maneira.
Isso normalmente é usado para retornos de chamada definidos assim em Objective-C:
typedef returnType (*SomeTypeDefinition) (int parameter1, NSString *parameter2);
Consulte também: BlockCallback.
Parâmetros
Você pode usar o [Params]
atributo no último parâmetro de matriz de uma definição de método para que o gerador injete um "parâmetros" na definição. Isso permite que a associação permita facilmente parâmetros opcionais.
Por exemplo, a seguinte definição:
[Export ("loadFiles:")]
void LoadFiles ([Params]NSUrl [] files);
Permite que o seguinte código seja escrito:
foo.LoadFiles (new NSUrl (url));
foo.LoadFiles (new NSUrl (url1), new NSUrl (url2), new NSUrl (url3));
Isso tem a vantagem adicional de não exigir que os usuários criem uma matriz apenas para passar elementos.
PlainString
Você pode usar o [PlainString]
atributo na frente dos parâmetros de cadeia de caracteres para instruir o gerador de associação a passar a cadeia de caracteres como uma cadeia de caracteres C, em vez de passar o parâmetro como um NSString
.
A maioria das Objective-C APIs consome NSString
parâmetros, mas algumas APIs expõem uma char *
API para passar cadeias de caracteres, em vez da NSString
variação.
Use [PlainString]
nesses casos.
Por exemplo, as seguintes Objective-C declarações:
- (void) setText: (NSString *) theText;
- (void) logMessage: (char *) message;
Deve ser encadernado assim:
[Export ("setText:")]
void SetText (string theText);
[Export ("logMessage:")]
void LogMessage ([PlainString] string theText);
RetainAttribute
Instrui o gerador a manter uma referência ao parâmetro especificado. O gerador fornecerá o repositório de backup para esse campo ou você poderá especificar um nome (o WrapName
) para armazenar o valor. Isso é útil para manter uma referência a um objeto gerenciado que é passado como um parâmetro para Objective-C e quando você sabe que Objective-C só manterá essa cópia do objeto. Por exemplo, uma API como SetDisplay (SomeObject)
usaria esse atributo, pois é provável que o SetDisplay só pudesse exibir um objeto por vez. Se você precisar acompanhar mais de um objeto (por exemplo, para uma API semelhante a uma pilha), use o [RetainList]
atributo.
Sintaxe:
public class RetainAttribute {
public RetainAttribute ();
public RetainAttribute (string wrapName);
public string WrapName { get; }
}
Atributo transitório
Esse atributo é aplicado a parâmetros e só é usado durante a transição para Objective-C C#. Durante essas transições, os vários Objective-CNSObject
parâmetros são encapsulados em uma representação gerenciada do objeto.
O runtime usará uma referência ao objeto nativo e manterá a referência até que a última referência gerenciada ao objeto desapareça e o GC tenha a chance de ser executado.
Em alguns casos, é importante que o runtime do C# não mantenha uma referência ao objeto nativo. Às vezes, isso acontece quando o código nativo subjacente anexou um comportamento especial ao ciclo de vida do parâmetro. Por exemplo: o destruidor do parâmetro executará alguma ação de limpeza ou descartará algum recurso precioso.
Esse atributo informa ao runtime que você deseja que o objeto seja descartado, se possível, ao retornar do Objective-C método substituído.
A regra é simples: se o runtime tiver que criar uma nova representação gerenciada do objeto nativo, no final da função, a contagem de retenção do objeto nativo será descartada e a propriedade Handle do objeto gerenciado será limpa. Isso significa que, se você mantiver uma referência ao objeto gerenciado, essa referência se tornará inútil (invocar métodos nele gerará uma exceção).
Se o objeto passado não tiver sido criado ou se já houver uma representação gerenciada pendente do objeto, o descarte forçado não ocorrerá.
Atributos da propriedade
Atributo NotImplementedNot
Esse atributo é usado para dar suporte a um Objective-C idioma em que uma propriedade com um getter é introduzida em uma classe base e uma subclasse mutável introduz um setter.
Como o C# não dá suporte a esse modelo, a classe base precisa ter o setter e o getter, e uma subclasse pode usar o OverrideAttribute.
Esse atributo é usado apenas em setters de propriedade e é usado para dar suporte ao idioma mutável em Objective-C.
Exemplo:
[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 enumeração
Mapear NSString
constantes para valores de enumeração é uma maneira fácil de criar uma API .NET melhor. It:
- permite que o auto-completar de código seja mais útil, mostrando apenas os valores corretos para a API;
- adiciona segurança de tipo, você não pode usar outra
NSString
constante em um contexto incorreto; e - permite ocultar algumas constantes, fazendo com que o auto-completar de código mostre uma lista de APIs mais curta sem perder a funcionalidade.
Exemplo:
enum NSRunLoopMode {
[DefaultEnumValue]
[Field ("NSDefaultRunLoopMode")]
Default,
[Field ("NSRunLoopCommonModes")]
Common,
[Field (null)]
Other = 1000
}
A partir da definição de ligação acima, o gerador criará o enum
próprio e também criará um *Extensions
tipo estático que inclui métodos de conversão bidirecional entre os valores de enumeração e as NSString
constantes. Isso significa que as constantes permanecem disponíveis para os desenvolvedores, mesmo que não façam parte da API.
Exemplos:
// 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
Você pode decorar um valor de enumeração com esse atributo. Isso se tornará a constante que está sendo retornada se o valor da enumeração não for conhecido.
Do exemplo acima:
var x = (NSRunLoopMode) 99;
Call (x.GetConstant ()); // NSDefaultRunLoopMode will be used
Se nenhum valor de enumeração for decorado, um NotSupportedException
será lançado.
ErrorDomainAttribute
Os códigos de erro são associados como valores de enumeração. Geralmente, há um domínio de erro para eles e nem sempre é fácil descobrir qual deles se aplica (ou se existe).
Você pode usar esse atributo para associar o domínio de erro à própria enumeração.
Exemplo:
[Native]
[ErrorDomain ("AVKitErrorDomain")]
public enum AVKitError : nint {
None = 0,
Unknown = -1000,
PictureInPictureStartFailed = -1001
}
Em seguida, você pode chamar o método GetDomain
de extensão para obter a constante de domínio de qualquer erro.
Atributo de campo
Esse é o mesmo [Field]
atributo usado para constantes dentro do tipo. Ele também pode ser usado dentro de enumerações para mapear um valor com uma constante específica.
Um null
valor pode ser usado para especificar qual valor de enumeração deve ser retornado se uma null
NSString
constante for especificada.
Do exemplo acima:
var constant = NSRunLoopMode.NewInWatchOS3; // will be null in watchOS 2.x
Call (NSRunLoopModeExtensions.GetValue (constant)); // will return 1000
Se nenhum null
valor estiver presente, um ArgumentNullException
será lançado.
Atributos globais
Os atributos globais são aplicados usando o modificador de [assembly:]
atributo como o [LinkWithAttribute]
ou podem ser usados em qualquer lugar, como os atributos de disponibilidade.
LinkWithAttribute
Esse é um atributo de nível de assembly que permite que os desenvolvedores especifiquem os sinalizadores de vinculação necessários para reutilizar uma biblioteca vinculada sem forçar o consumidor da biblioteca a configurar manualmente os argumentos gcc_flags e mtouch extras passados para uma biblioteca.
Sintaxe:
// 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; }
}
Esse atributo é aplicado no nível do assembly, por exemplo, é isso que as associações CorePlot usam:
[assembly: LinkWith ("libCorePlot-CocoaTouch.a", LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator, Frameworks = "CoreGraphics QuartzCore", ForceLoad = true)]
Quando você usa o [LinkWith]
atributo, o especificado libraryName
é inserido no assembly resultante, permitindo que os usuários enviem uma única DLL que contém as dependências não gerenciadas, bem como os sinalizadores de linha de comando necessários para consumir corretamente a biblioteca do Xamarin.iOS.
Também é possível não fornecer um libraryName
, caso em que o LinkWith
atributo pode ser usado para especificar apenas sinalizadores de vinculador adicionais:
[assembly: LinkWith (LinkerFlags = "-lsqlite3")]
Construtores LinkWithAttribute
Esses construtores permitem que você especifique a biblioteca a ser vinculada e inserida no assembly resultante, os destinos com suporte aos quais a biblioteca dá suporte e quaisquer sinalizadores de biblioteca opcionais necessários para vincular à biblioteca.
Observe que o LinkTarget
argumento é inferido pelo Xamarin.iOS e não precisa ser definido.
Exemplos:
// 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
A ForceLoad
propriedade é usada para decidir se o sinalizador de -force_load
link é ou não usado para vincular a biblioteca nativa. Por enquanto, isso deve ser sempre verdade.
LinkWithAttribute.Frameworks
Se a biblioteca que está sendo vinculada tiver um requisito rígido em qualquer estrutura (diferente de Foundation
e UIKit
), você deverá definir a Frameworks
propriedade como uma cadeia de caracteres contendo uma lista delimitada por espaço das estruturas de plataforma necessárias. Por exemplo, se você estiver vinculando uma biblioteca que requer CoreGraphics
e CoreText
, você definiria a Frameworks
propriedade como "CoreGraphics CoreText"
.
LinkWithAttribute.IsCxx
Defina essa propriedade como true se o executável resultante precisar ser compilado usando um compilador C++ em vez do padrão, que é um compilador C. Use isso se a biblioteca que você está associando foi escrita em C++.
LinkWithAttribute.LibraryName
O nome da biblioteca não gerenciada a ser agrupada. Este é um arquivo com a extensão ".a" e pode conter código objeto para várias plataformas (por exemplo, ARM e x86 para o simulador).
Versões anteriores do Xamarin.iOS verificaram a LinkTarget
propriedade para determinar a plataforma compatível com sua biblioteca, mas agora isso é detectado automaticamente e a LinkTarget
propriedade é ignorada.
LinkWithAttribute.LinkerFlags
A LinkerFlags
cadeia de caracteres fornece uma maneira para os autores de associação especificarem quaisquer sinalizadores de vinculador adicionais necessários ao vincular a biblioteca nativa ao aplicativo.
Por exemplo, se a biblioteca nativa exigir libxml2 e zlib, você definirá a LinkerFlags
string como "-lxml2 -lz"
.
LinkWithAttribute.LinkTarget
Versões anteriores do Xamarin.iOS verificaram a LinkTarget
propriedade para determinar a plataforma compatível com sua biblioteca, mas agora isso é detectado automaticamente e a LinkTarget
propriedade é ignorada.
LinkWithAttribute.NeedsGccExceptionHandling
Defina essa propriedade como true se a biblioteca que você está vinculando exigir a biblioteca de Tratamento de Exceções do GCC (gcc_eh)
LinkWithAttribute.SmartLink
A SmartLink
propriedade deve ser definida como true para permitir que o Xamarin.iOS determine se ForceLoad
é necessário ou não.
LinkWithAttribute.WeakFrameworks
A WeakFrameworks
propriedade funciona da mesma maneira que a Frameworks
propriedade, exceto que, no momento do link, o especificador é passado para o -weak_framework
gcc para cada uma das estruturas listadas.
WeakFrameworks
possibilita que bibliotecas e aplicativos se vinculem fracamente às estruturas da plataforma para que possam usá-las opcionalmente se estiverem disponíveis, mas não tenham uma dependência rígida delas, o que é útil se sua biblioteca se destina a adicionar recursos extras em versões mais recentes do iOS. Para obter mais informações sobre links fracos, consulte a documentação da Apple sobre links fracos.
Bons candidatos para links fracos seriam Frameworks
como Contas, CoreBluetooth
, CoreImage
, GLKit
, NewsstandKit
e Twitter
uma vez que estão disponíveis apenas no iOS 5.
Atributo de conselho
Use esse atributo para dar aos desenvolvedores uma dica sobre outras APIs que podem ser mais convenientes para eles usarem. Por exemplo, se você fornecer uma versão fortemente tipada de uma API, poderá usar esse atributo no atributo fracamente tipado para direcionar o desenvolvedor para a API melhor.
As informações desse atributo são mostradas na documentação e as ferramentas podem ser desenvolvidas para dar sugestões aos usuários sobre como melhorar
RequiresSuperAttribute
Essa é uma subclasse especializada do [Advice]
atributo que pode ser usada para sugerir ao desenvolvedor que a substituição de um método requer uma chamada para o método base (substituído).
Isso corresponde a clang
__attribute__((objc_requires_super))
Atributo ZeroCopyStrings
Disponível apenas no Xamarin.iOS 5.4 e mais recente.
Esse atributo instrui o gerador de que a associação para essa biblioteca específica (se aplicada com [assembly:]
) ou tipo deve usar o marshaling rápido de cadeia de caracteres de cópia zero. Esse atributo é equivalente a passar a opção --zero-copy
de linha de comando para o gerador.
Ao usar zero-copy para cadeias de caracteres, o gerador usa efetivamente a mesma cadeia de caracteres C# que a cadeia de caracteres que Objective-C consome sem incorrer na criação de um novo NSString
objeto e evitando copiar os dados das cadeias de caracteres C# para a Objective-C cadeia de caracteres. A única desvantagem de usar cadeias de caracteres de cópia zero é que você deve garantir que qualquer propriedade de cadeia de caracteres que você encapsular que por acaso seja sinalizada como retain
ou copy
tenha o [DisableZeroCopy]
atributo definido. Isso é necessário porque o identificador para cadeias de caracteres de cópia zero é alocado na pilha e é inválido no retorno da função.
Exemplo:
[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; }
}
Você também pode aplicar o atributo no nível do assembly e ele se aplicará a todos os tipos de assembly:
[assembly:ZeroCopyStrings]
Dicionários fortemente tipados
Com o Xamarin.iOS 8.0, introduzimos suporte para criar facilmente classes fortemente tipadas que encapsulam NSDictionaries
.
Embora sempre tenha sido possível usar o tipo de dados DictionaryContainer junto com uma API manual, agora é muito mais simples fazer isso. Para obter mais informações, consulte Exibindo tipos fortes.
Dicionário Forte
Quando esse atributo é aplicado a uma interface, o gerador produzirá uma classe com o mesmo nome que a interface que deriva de DictionaryContainer e transforma cada propriedade definida na interface em um getter e setter fortemente tipado para o dicionário.
Isso gera automaticamente uma classe que pode ser instanciada a partir de uma existente NSDictionary
ou que foi criada nova.
Esse atributo usa um parâmetro, o nome da classe que contém as chaves que são usadas para acessar os elementos no dicionário. Por padrão, cada propriedade na interface com o atributo pesquisará um membro no tipo especificado para um nome com o sufixo "Key".
Por exemplo:
[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; }
}
No caso acima, a MyOption
classe produzirá uma propriedade de string para Name
que usará o MyOptionKeys.NameKey
como a chave no dicionário para recuperar uma string. E usará o MyOptionKeys.AgeKey
como a chave no dicionário para recuperar um NSNumber
que contém um int.
Se você quiser usar uma chave diferente, poderá usar o atributo export na propriedade, por exemplo:
[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 dicionário fortes
Os seguintes tipos de dados são suportados na StrongDictionary
definição:
Tipo de interface C# | NSDictionary Tipo de armazenamento |
---|---|
bool |
Boolean armazenado em um NSNumber |
Valores de enumeração | inteiro armazenado em um NSNumber |
int |
inteiro de 32 bits armazenado em um NSNumber |
uint |
inteiro sem sinal de 32 bits armazenado em um NSNumber |
nint |
NSInteger armazenado em um NSNumber |
nuint |
NSUInteger armazenado em um NSNumber |
long |
inteiro de 64 bits armazenado em um NSNumber |
float |
inteiro de 32 bits armazenado como um NSNumber |
double |
inteiro de 64 bits armazenado como um NSNumber |
NSObject e subclasses |
NSObject |
NSDictionary |
NSDictionary |
string |
NSString |
NSString |
NSString |
C# Array de NSObject |
NSArray |
C# Array de enumerações |
NSArray contendo NSNumber valores |