Como o Xamarin.Mac funciona

Na maioria das vezes, o desenvolvedor nunca terá que se preocupar com a "mágica" interna do Xamarin.Mac, no entanto, ter uma compreensão aproximada de como as coisas funcionam nos bastidores ajudará na interpretação da documentação existente com uma lente C# e na depuração de problemas quando eles surgirem.

No Xamarin.Mac, um aplicativo une dois mundos: há o Objective-C runtime baseado que contém instâncias de classes nativas (NSString, NSApplication, etc) e há o runtime C# que contém instâncias de classes gerenciadas (System.String, HttpClient, etc). Entre esses dois mundos, o Xamarin.Mac cria uma ponte bidirecional para que um aplicativo possa chamar métodos (seletores) em Objective-C (como NSApplication.Init) e Objective-C possa chamar os métodos C# do aplicativo de volta (como métodos em um representante de aplicativo). Em geral, as chamadas são Objective-C tratadas de forma transparente por meio de P/Invokes e algum código de runtime fornecido pelo Xamarin.

Expondo classes/métodos C# a Objective-C

No entanto, para Objective-C chamar de volta os objetos C# de um aplicativo, eles precisam ser expostos de uma maneira que Objective-C possa entender. Isso é feito por meio dos Register atributos e Export . Veja o exemplo seguinte:

[Register ("MyClass")]
public class MyClass : NSObject
{
   [Export ("init")]
   public MyClass ()
   {
   }

   [Export ("run")]
   public void Run ()
   {
   }
}

Neste exemplo, o tempo de Objective-C execução agora saberá sobre uma classe chamada MyClass com seletores chamados init e run.

Na maioria dos casos, esse é um detalhe de implementação que o desenvolvedor pode ignorar, pois a maioria dos retornos de chamada que um aplicativo recebe será por meio de métodos substituídos em base classes (como AppDelegate, Delegates, DataSources) ou em ações passadas para APIs. Em todos esses casos, Export os atributos não são necessários no código C#.

Execução do construtor

Em muitos casos, o desenvolvedor precisará expor a API de construção de classes C# do aplicativo ao Objective-C runtime para que ela possa ser instanciada de locais como quando chamada em arquivos Storyboard ou XIB. Aqui estão os cinco construtores mais comuns usados em aplicativos Xamarin.Mac:

// Called when created from unmanaged code
public CustomView (IntPtr handle) : base (handle)
{
   Initialize ();
}

// Called when created directly from a XIB file
[Export ("initWithCoder:")]
public CustomView (NSCoder coder) : base (coder)
{
   Initialize ();
}

// Called from C# to instance NSView with a Frame (initWithFrame)
public CustomView (CGRect frame) : base (frame)
{
}

// Called from C# to instance NSView without setting the frame (init)
public CustomView () : base ()
{
}

// This is a special case constructor that you call on a derived class when the derived called has an [Export] constructor.
// For example, if you call init on NSString then you don’t want to call init on NSObject.
public CustomView () : base (NSObjectFlag.Empty)
{
}

Em geral, o desenvolvedor deve deixar os IntPtr construtores e NSCoder gerados ao criar alguns tipos, como custom, NSViews sozinhos. Se o Xamarin.Mac precisar chamar um desses construtores em resposta a uma Objective-C solicitação de runtime e você o tiver removido, o aplicativo falhará dentro do código nativo e poderá ser difícil descobrir exatamente o problema.

Gerenciamento de memória e ciclos

O gerenciamento de memória no Xamarin.Mac é, em muitos aspectos, muito semelhante ao Xamarin.iOS. Também é um tópico complexo, além do escopo deste documento. Leia as práticas recomendadas de memória e desempenho.

Compilação antecipada

Normalmente, os aplicativos .NET não são compilados para o código de máquina quando são criados, em vez disso, eles são compilados para uma camada intermediária chamada código IL que obtém o JIT (Just-In-Time ) compilado para o código de máquina quando o aplicativo é iniciado.

O tempo que leva para o runtime mono compilar esse código de computador pode retardar a inicialização de um aplicativo Xamarin.Mac em até 20%, pois leva tempo para que o código de computador necessário seja gerado.

Devido às limitações impostas pela Apple no iOS, a compilação JIT do código IL não está disponível para o Xamarin.iOS. Como resultado, todos os aplicativos Xamarin.iOS são AOT (Ahead-Of-Time) completos compilados para código de computador durante o ciclo de build.

Uma novidade no Xamarin.Mac é a capacidade de AOT o código IL durante o ciclo de build do aplicativo, assim como o Xamarin.iOS pode. O Xamarin.Mac usa uma abordagem AOT híbrida que compila a maioria do código de computador necessário, mas permite que o runtime compile os trampolins necessários e a flexibilidade para continuar a dar suporte a Reflection.Emit (e outros casos de uso que atualmente funcionam no Xamarin.Mac).

Há duas áreas principais em que o AOT pode ajudar um aplicativo Xamarin.Mac:

  • Melhores logs de falhas "nativos" – se um aplicativo Xamarin.Mac falhar no código nativo, o que é comum ao fazer chamadas inválidas em APIs do Cocoa (como enviar um null para um método que não o aceita), os logs de falhas nativos com quadros JIT serão difíceis de analisar. Como os quadros JIT não têm informações de depuração, haverá várias linhas com deslocamentos hexadecimais e nenhuma pista do que estava acontecendo. AOT gera quadros nomeados "reais" e os traços são muito mais fáceis de ler. Isso também significa que o aplicativo Xamarin.Mac interagirá melhor com ferramentas nativas, como lldb e Instruments.
  • Melhor desempenho de tempo de inicialização – para aplicativos Xamarin.Mac grandes, com um tempo de inicialização de vários segundos, a compilação JIT de todo o código pode levar um tempo significativo. AOT faz esse trabalho antecipadamente.

Habilitando a compilação AOT

O AOT é habilitado no Xamarin.Mac clicando duas vezes no Nome do Projeto no Gerenciador de Soluções, navegando até o Build do Mac e adicionando --aot:[options] ao campo Argumentos mmp adicionais: (onde [options] está uma ou mais opções para controlar o tipo AOT, veja abaixo). Por exemplo:

Adicionando AOT a argumentos mmp adicionais

Importante

Habilitar a compilação AOT aumenta drasticamente o tempo de compilação, às vezes até vários minutos, mas pode melhorar o tempo de inicialização do aplicativo em uma média de 20%. Como resultado, a compilação AOT só deve ser habilitada em builds de versão de um aplicativo Xamarin.Mac.

Opções de compilação Aot

Há várias opções diferentes que podem ser ajustadas ao habilitar a compilação AOT em um aplicativo Xamarin.Mac:

  • none - Nenhuma compilação AOT. Esta é a configuração padrão.
  • all - AOT compila todos os assemblies no MonoBundle.
  • core - AOT compila os Xamarin.Macassemblies , System e mscorlib .
  • sdk - AOT compila os Xamarin.Mac assemblies e BCL (Bibliotecas de Classes Base).
  • |hybrid - Adicionar isso a uma das opções acima permite AOT híbrido que permite a remoção de IL, mas resultará em tempos de compilação mais longos.
  • + - Inclui um único arquivo para compilação AOT.
  • - - Remove um único arquivo da compilação AOT.

Por exemplo, habilitaria a compilação AOT em todos os assemblies no MonoBundle, --aot:all,-MyAssembly.dll exceto MyAssembly.dll e --aot:core|hybrid,+MyOtherAssembly.dll,-mscorlib.dll habilitaria o código AOT híbrido inclui o e excluindo o MyOtherAssembly.dll mscorlib.dll.

Estático parcial registrar

Ao desenvolver um aplicativo Xamarin.Mac, minimizar o tempo entre a conclusão de uma alteração e o teste pode se tornar importante para cumprir os prazos de desenvolvimento. Estratégias como modularização de bases de código e testes de unidade podem ajudar a diminuir os tempos de compilação, pois reduzem o número de vezes que um aplicativo exigirá uma reconstrução completa cara.

Além disso, e novo no Xamarin.Mac, o Partial Static Registrar (conforme pioneiro do Xamarin.iOS) pode reduzir drasticamente os tempos de inicialização de um aplicativo Xamarin.Mac na configuração de depuração. Entender como o uso do Partial Static Registrar pode espremer uma melhoria de quase 5x no lançamento de depuração exigirá um pouco de informações sobre o registrar que é, qual é a diferença entre estático e dinâmico e o que essa versão "estática parcial" faz.

Sobre o registrar

Sob o capô de qualquer aplicativo Xamarin.Mac está a estrutura Cocoa da Apple e o Objective-C runtime. Construir uma ponte entre esse "mundo nativo" e o "mundo gerenciado" do C# é a principal responsabilidade do Xamarin.Mac. Parte dessa tarefa é tratada pelo registrarmétodo , que é executado dentro NSApplication.Init () . Esse é um motivo pelo qual qualquer uso de APIs Cocoa no Xamarin.Mac requer ser NSApplication.Init chamado primeiro.

O registrartrabalho do é informar o Objective-C runtime sobre a existência das classes C# do aplicativo que derivam de classes como NSApplicationDelegate, NSView, NSWindowe NSObject. Isso requer uma verificação de todos os tipos no aplicativo para determinar o que precisa ser registrado e quais elementos de cada tipo devem ser relatados.

Essa verificação pode ser feita dinamicamente, na inicialização do aplicativo com reflexão, ou estaticamente, como uma etapa de tempo de compilação. Ao escolher um tipo de registro, o desenvolvedor deve estar ciente do seguinte:

  • O registro estático pode reduzir drasticamente os tempos de inicialização, mas pode diminuir significativamente os tempos de compilação (normalmente mais do que o dobro do tempo de compilação de depuração). Esse será o padrão para builds de configuração de versão .
  • O registro dinâmico atrasa esse trabalho até a inicialização do aplicativo e ignora a geração de código, mas esse trabalho adicional pode criar uma pausa perceptível (pelo menos dois segundos) na inicialização do aplicativo. Isso é especialmente perceptível em builds de configuração de depuração, que usam como padrão o registro dinâmico e cuja reflexão é mais lenta.

O Registro Estático Parcial, introduzido pela primeira vez no Xamarin.iOS 8.13, oferece ao desenvolvedor o melhor das duas opções. Ao pré-computar as informações de registro de cada elemento e Xamarin.Mac.dll enviar essas informações com o Xamarin.Mac em uma biblioteca estática (que só precisa ser vinculada no momento da compilação), a Microsoft removeu a maior parte do tempo de reflexão da dinâmica registrar , sem afetar o tempo de compilação.

Habilitando a estática parcial registrar

A Estática Parcial Registrar é habilitada no Xamarin.Mac clicando duas vezes no Nome do Projeto no Gerenciador de Soluções, navegando até o Build do Mac e adicionando --registrar:static ao campo Argumentos mmp adicionais:. Por exemplo:

Adicionando a estática registrar parcial a argumentos mmp adicionais

Recursos adicionais

Aqui estão algumas explicações mais detalhadas de como as coisas funcionam internamente: