Partilhar via


Tipo registrar para Xamarin.iOS

Este documento descreve o sistema de registro de tipos usado pelo Xamarin.iOS.

Registro de classes e métodos gerenciados

Durante a inicialização, o Xamarin.iOS será registrado:

  • Classes com um atributo [Registrar] como Objective-C classes.
  • Classes com um atributo [Categoria] como Objective-C categorias.
  • Interfaces com um atributo [Protocolo] como Objective-C protocolos.
  • Membros com um [Exportar], tornando possível Objective-C acessá-los.

Por exemplo, considere o método gerenciado Main comum em aplicativos Xamarin.iOS:

UIApplication.Main (args, null, "AppDelegate");

Esse código informa ao Objective-C runtime para usar o tipo chamado AppDelegate como classe delegada do aplicativo. Para que o Objective-C runtime possa criar uma instância da classe C# AppDelegate , essa classe deve ser registrada.

O Xamarin.iOS executa o registro automaticamente, seja em runtime (registro dinâmico) ou em tempo de compilação (registro estático).

O registro dinâmico usa reflexão na inicialização para localizar todas as classes e métodos a serem registrados, passando-os para o Objective-C runtime. O registro dinâmico é usado por padrão para builds de simulador.

O registro estático inspeciona, em tempo de compilação, os assemblies usados pelo aplicativo. Ele determina as classes e os métodos com Objective-C os quais se registrar e gera um mapa, que é inserido em seu binário. Em seguida, na inicialização, ele registra o mapa com o Objective-C runtime. O registro estático é usado para builds de dispositivo.

Categorias

A partir do Xamarin.iOS 8.10, é possível criar Objective-C categorias usando a sintaxe C#.

Para criar uma categoria, use o [Category] atributo e especifique o tipo a ser estendido. Por exemplo, o código a seguir estende NSString:

[Category (typeof (NSString))]

Cada um dos métodos de uma categoria tem um [Export] atributo, disponibilizando-o para o Objective-C runtime:

[Export ("today")]
public static string Today ()
{
    return "Today";
}

Todos os métodos de extensão gerenciados devem ser estáticos, mas é possível criar Objective-C métodos de instância usando a sintaxe C# padrão para métodos de extensão:

[Export ("toUpper")]
public static string ToUpper (this NSString self)
{
    return self.ToString ().ToUpper ();
}

O primeiro argumento para o método de extensão é a instância na qual o método foi invocado:

[Category (typeof (NSString))]
public static class MyStringCategory
{
    [Export ("toUpper")]
    static string ToUpper (this NSString self)
    {
        return self.ToString ().ToUpper ();
    }
 }

Este exemplo adicionará um método de instância nativa toUpper à NSString classe . Esse método pode ser chamado de Objective-C:

[Category (typeof (UIViewController))]
public static class MyViewControllerCategory
{
    [Export ("shouldAutoRotate")]
    static bool GlobalRotate ()
    {
        return true;
    }
}

Protocolos

A partir do Xamarin.iOS 8.10, as interfaces com o [Protocol] atributo serão exportadas para Objective-C como protocolos:

[Protocol ("MyProtocol")]
interface IMyProtocol
{
    [Export ("method")]
    void Method ();
}

class MyClass : IMyProtocol
{
    void Method ()
    {
    }
}

Esse código exporta IMyProtocol para Objective-C como um protocolo chamado MyProtocol e uma classe chamada MyClass que implementa o protocolo.

Novo sistema de registro

Começando com a versão estável 6.2.6 e a versão beta 6.3.4, adicionamos um novo estático registrar. Na versão 7.2.1, tornamos o novo registrar padrão.

Este novo sistema de registro oferece os seguintes novos recursos:

  • Detecção em tempo de compilação de erros do programador:

    • Duas classes sendo registradas com o mesmo nome.
    • Mais de um método exportado para responder ao mesmo seletor
  • Remoção de código nativo não utilizado:

    • O novo sistema de registro adicionará referências fortes ao código usado em bibliotecas estáticas, permitindo que o vinculador nativo remova o código nativo não utilizado do binário resultante. Nas associações de exemplo do Xamarin, a maioria dos aplicativos se torna pelo menos 300 mil menor.
  • Suporte para subclasses genéricas do NSObject; consulte NSObject Generics para obter mais informações. Além disso, o novo sistema de registro capturará construções genéricas sem suporte que anteriormente teriam causado comportamento aleatório em runtime.

Erros capturados pelo novo registrar

Abaixo estão alguns exemplos dos erros capturados pelo novo registrar.

  • Exportando o mesmo seletor mais de uma vez na mesma classe:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo:")]
        void Foo (NSString str);
        [Export ("foo:")]
        void Foo (string str)
    }
    
  • Exportando mais de uma classe gerenciada com o mesmo Objective-C nome:

    [Register ("Class")]
    class MyClass : NSObject {}
    
    [Register ("Class")]
    class YourClass : NSObject {}
    
  • Exportando métodos genéricos:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo")]
        void Foo<T> () {}
    }
    

Limitações do novo registrar

Algumas coisas a ter em mente sobre o novo registrar:

  • Algumas bibliotecas de terceiros devem ser atualizadas para trabalhar com o novo sistema de registro. Confira as modificações necessárias abaixo para obter mais detalhes.

  • Uma desvantagem de curto prazo também é que clang deve ser usado se a estrutura Contas for usada (isso ocorre porque o cabeçalho accounts.h da Apple só pode ser compilado pelo Clang). Adicione --compiler:clang aos argumentos mtouch adicionais para usar Clang se você estiver usando o Xcode 4.6 ou anterior (o Xamarin.iOS selecionará automaticamente Clang no Xcode 5.0 ou posterior.)

  • Se o Xcode 4.6 (ou anterior) for usado, GCC/G++ deverá ser selecionado se os nomes de tipo exportados contiverem caracteres não ASCII (isso ocorre porque a versão do Clang enviada com xcode 4.6 não dá suporte a caracteres não ASCII dentro de identificadores no Objective-C código). Adicione --compiler:gcc aos argumentos mtouch adicionais para usar o GCC.

Selecionando um registrar

Você pode selecionar um diferente registrar adicionando uma das seguintes opções aos argumentos mtouch adicionais nas configurações de build do iOS do projeto:

  • --registrar:static – padrão para builds de dispositivo
  • --registrar:dynamic – padrão para builds de simulador

Observação

A API Clássica do Xamarin tinha suporte para outras opções, como --registrar:legacystatic e --registrar:legacydynamic. No entanto, essas opções não são compatíveis com a API Unificada.

Deficiências no sistema de registro antigo

O sistema de registro antigo tem as seguintes desvantagens:

  • Não havia nenhuma referência estática (nativa) a Objective-C classes e métodos em bibliotecas nativas de terceiros, o que significava que não poderíamos pedir ao vinculador nativo para remover o código nativo de terceiros que não foi realmente usado (porque tudo seria removido). Esse é o motivo pelo -force_load libNative.a qual cada associação de terceiros tinha que fazer (ou o equivalente ForceLoad=true no [LinkWith] atributo).
  • Você pode exportar dois tipos gerenciados com o mesmo Objective-C nome sem aviso. Um cenário raro era acabar com duas AppDelegate classes em namespaces diferentes. No runtime, seria completamente aleatório qual foi escolhido (na verdade, ele variou entre execuções de um aplicativo que nem sequer foi reconstruído - o que fez uma experiência de depuração muito intrigante e frustrante).
  • Você pode exportar dois métodos com a mesma Objective-C assinatura. Mais uma vez, qual seria chamado de Objective-C era aleatório (mas esse problema não era tão comum quanto o anterior, principalmente porque a única maneira de realmente experimentar esse bug era substituir o método gerenciado azarado).
  • O conjunto de métodos que foi exportado foi ligeiramente diferente entre builds dinâmicos e estáticos.
  • Ele não funciona corretamente ao exportar classes genéricas (qual implementação genérica exata executada em runtime seria aleatória, resultando efetivamente em comportamento indeterminado).

Novo registrar: alterações necessárias nas associações

Esta seção descreve as alterações de associações que devem ser feitas para trabalhar com o novo registrar.

Os protocolos devem ter o atributo [Protocolo]

Os protocolos agora devem ter o [Protocol] atributo . Se você não fizer isso, um erro de vinculador nativo, como:

Undefined symbols for architecture i386: "_OBJC_CLASS_$_ProtocolName", referenced from: ...

Os seletores devem ter um número válido de parâmetros

Todos os seletores devem indicar o número de parâmetros corretamente. Anteriormente, esses erros eram ignorados e podiam causar problemas de runtime.

Em suma, o número de dois-pontos deve corresponder ao número de parâmetros:

  • Sem parâmetros: foo
  • Um parâmetro: foo:
  • Dois parâmetros: foo:parameterName2:

Veja a seguir os usos incorretos:

// Invalid: export takes no arguments, but function expects one
[Export ("apply")]
void Apply (NSObject target);

// Invalid: exported as taking an argument, but the managed version does not have one:
[Export ("display:")]
void Display ();

Usar o parâmetro IsVariadic em Exportar

As funções variádicas devem usar o IsVariadic argumento para o [Export] atributo :

[Export ("variadicMethod:", IsVariadic = true)]
void VariadicMethod (NSObject first, IntPtr subsequent);

É impossível associar classes que não existem na biblioteca nativa. Se uma classe tiver sido removida ou renomeada na biblioteca nativa, atualize as associações para corresponder.