Share via


Visão geral das associações Objective-C

Detalhes de como o processo de associação funciona

Associar uma Objective-C biblioteca para uso com o Xamarin executa três etapas:

  1. Escreva uma "definição de API" em C# para descrever como a API nativa é exposta no .NET e como ela é mapeada para o subjacente Objective-C. Isso é feito usando constructos C# padrão, como interface e vários atributos de associação (consulte este exemplo simples).

  2. Depois de escrever a "definição de API" em C#, compile-a para produzir um assembly de "associação". Isso pode ser feito na linha de comando ou usando um projeto de associação no Visual Studio para Mac ou no Visual Studio.

  3. Esse assembly de "associação" é adicionado ao seu projeto de aplicativo Xamarin, para que você possa acessar a funcionalidade nativa usando a API que você definiu. O projeto de associação é completamente separado de seus projetos de aplicativo.

    Observação

    A etapa 1 pode ser automatizada com a ajuda do Objective Sharpie. Ele examina a Objective-C API e gera uma "definição de API" em C# proposta. Você pode personalizar os arquivos criados pelo Objective Sharpie e usá-los em um projeto de associação (ou na linha de comando) para criar o assembly de associação. Objective Sharpie não cria associações por si só, é apenas uma parte opcional do processo maior.

Você também pode ler mais detalhes técnicos de como ele funciona, o que ajudará você a escrever suas associações.

Associações de linha de comando

Você pode usar o btouch-native para Xamarin.iOS (ou bmac-native se estiver usando o Xamarin.Mac) para criar associações diretamente. Ele funciona passando as definições de API C# que você criou manualmente (ou usando Objective Sharpie) para a ferramenta de linha de comando (btouch-native para iOS ou bmac-native para Mac).

A sintaxe geral para invocar essas ferramentas é:

# Use this for Xamarin.iOS:
bash$ /Developer/MonoTouch/usr/bin/btouch-native -e cocos2d.cs -s:enums.cs -x:extensions.cs
# Use this for Xamarin.Mac:
bash$ bmac-native -e cocos2d.cs -s:enums.cs -x:extensions.cs

O comando acima gerará o arquivo cocos2d.dll no diretório atual e conterá a biblioteca totalmente associada que você pode usar em seu projeto. Essa é a ferramenta que Visual Studio para Mac usa para criar suas associações se você usar um projeto de associação (descrito abaixo).

Projeto de associação

Um projeto de associação pode ser criado no Visual Studio para Mac ou no Visual Studio (o Visual Studio dá suporte apenas a associações iOS) e facilita a edição e a criação de definições de API para associação (em vez de usar a linha de comando).

Siga este guia de introdução para ver como criar e usar um projeto de associação para produzir uma associação.

Objective Sharpie

Objective Sharpie é outra ferramenta de linha de comando separada que ajuda com os estágios iniciais da criação de uma associação. Ele não cria uma associação por si só, mas automatiza a etapa inicial de geração de uma definição de API para a biblioteca nativa de destino.

Leia os documentos do Objective Sharpie para saber como analisar bibliotecas nativas, estruturas nativas e CocoaPods em definições de API que podem ser incorporadas a associações.

Como funciona a associação

É possível usar o atributo [Register], o atributo [Export] e a invocação de seletor manual Objective-C para associar manualmente novos tipos (anteriormente não associadosObjective-C).

Primeiro, encontre um tipo que você deseja associar. Para fins de discussão (e simplicidade), associaremos o tipo NSEnumerator (que já foi associado em Foundation.NSEnumerator; a implementação abaixo é apenas para fins de exemplo).

Em segundo lugar, precisamos criar o tipo C#. Provavelmente, queremos colocar isso em um namespace; Como Objective-C não dá suporte a namespaces, precisaremos usar o [Register] atributo para alterar o nome do tipo que o Xamarin.iOS registrará com o Objective-C runtime. O tipo C# também deve herdar de Foundation.NSObject:

namespace Example.Binding {
    [Register("NSEnumerator")]
    class NSEnumerator : NSObject
    {
        // see steps 3-5
    }
}

Em terceiro lugar, examine a Objective-C documentação e crie instâncias de ObjCRuntime.Selector para cada seletor que você deseja usar. Coloque-os dentro do corpo da classe:

static Selector selInit       = new Selector("init");
static Selector selAllObjects = new Selector("allObjects");
static Selector selNextObject = new Selector("nextObject");

Em quarto lugar, seu tipo precisará fornecer construtores. Você deve encadear a invocação do construtor ao construtor de classe base. Os [Export] atributos permitem que Objective-C o código chame os construtores com o nome do seletor especificado:

[Export("init")]
public NSEnumerator()
    : base(NSObjectFlag.Empty)
{
    Handle = Messaging.IntPtr_objc_msgSend(this.Handle, selInit.Handle);
}
// This constructor must be present so that Xamarin.iOS
// can create instances of your type from Objective-C code.
public NSEnumerator(IntPtr handle)
    : base(handle)
{
}

Em quinto lugar, forneça métodos para cada um dos Seletores declarados na Etapa 3. Elas serão usadas objc_msgSend() para invocar o seletor no objeto nativo. Observe o uso de Runtime.GetNSObject() para converter um IntPtr em um tipo (sub)tipado NSObject adequadamente. Se você quiser que o método seja chamado do Objective-C código, o membro deverá ser virtual.

[Export("nextObject")]
public virtual NSObject NextObject()
{
    return Runtime.GetNSObject(
        Messaging.IntPtr_objc_msgSend(this.Handle, selNextObject.Handle));
}
// Note that for properties, [Export] goes on the get/set method:
public virtual NSArray AllObjects {
    [Export("allObjects")]
    get {
        return (NSArray) Runtime.GetNSObject(
            Messaging.IntPtr_objc_msgSend(this.Handle, selAllObjects.Handle));
    }
}

Juntando tudo:

using System;
using Foundation;
using ObjCRuntime;

namespace Example.Binding {
    [Register("NSEnumerator")]
    class NSEnumerator : NSObject
    {
        static Selector selInit       = new Selector("init");
        static Selector selAllObjects = new Selector("allObjects");
        static Selector selNextObject = new Selector("nextObject");

        [Export("init")]
        public NSEnumerator()
            : base(NSObjectFlag.Empty)
        {
            Handle = Messaging.IntPtr_objc_msgSend(this.Handle,
                selInit.Handle);
        }

        public NSEnumerator(IntPtr handle)
            : base(handle)
        {
        }

        [Export("nextObject")]
        public virtual NSObject NextObject()
        {
            return Runtime.GetNSObject(
                Messaging.IntPtr_objc_msgSend(this.Handle,
                    selNextObject.Handle));
        }

        // Note that for properties, [Export] goes on the get/set method:
        public virtual NSArray AllObjects {
            [Export("allObjects")]
            get {
                return (NSArray) Runtime.GetNSObject(
                    Messaging.IntPtr_objc_msgSend(this.Handle,
                        selAllObjects.Handle));
            }
        }
    }
}