Objective-C - Objective-C

Objective-C
Família C
Projetado por Tom Love e Brad Cox
Apareceu pela primeira vez 1984 ; 37 anos atrás ( 1984 )
Versão estável
2.0
Disciplina de digitação estático , dinâmico , fraco
SO Plataforma cruzada
Extensões de nome de arquivo .h, .m, .mm, .M
Local na rede Internet developer.apple.com
Implementações principais
Clang , GCC
Influenciado por
C , Smalltalk
Influenciado
Groovy , Java , Nu , Objective-J , TOM , Swift

Objective-C é um de uso geral , orientada a objeto linguagem de programação que adiciona Smalltalk estilo de mensagens para o C linguagem de programação. Originalmente desenvolvido por Brad Cox e Tom Love no início dos anos 1980, foi selecionado pela NeXT para seu sistema operacional NeXTSTEP . Objective-C era a linguagem de programação padrão suportada pela Apple para desenvolver macOS (que descendia do NeXTSTEP) e aplicativos iOS usando suas respectivas interfaces de programação de aplicativos (APIs), Cocoa e Cocoa Touch , até a introdução do Swift em 2014.

Os programas Objective-C desenvolvidos para sistemas operacionais não Apple ou que não dependem das APIs da Apple também podem ser compilados para qualquer plataforma suportada pelo GNU GCC ou LLVM / Clang .

Os arquivos de programa de 'mensagem / implementação' do código-fonte Objective-C geralmente têm extensões de nome de arquivo .m , enquanto os arquivos 'cabeçalho / interface' Objective-C têm extensões .h , iguais aos arquivos de cabeçalho C. Os arquivos Objective-C ++ são indicados com uma extensão de arquivo .mm .

História

Objective-C foi criado principalmente por Brad Cox e Tom Love no início dos anos 1980 em sua empresa Productivity Products International (PPI) .

Antes da criação de sua empresa, ambos foram apresentados ao Smalltalk enquanto estavam no Centro de Tecnologia de Programação da ITT Corporation em 1981. Os primeiros trabalhos em rastreamentos de Objective-C datavam dessa época. Cox ficou intrigado com os problemas de verdadeira reutilização em design e programação de software . Ele percebeu que uma linguagem como Smalltalk seria inestimável na construção de ambientes de desenvolvimento para desenvolvedores de sistema na ITT. No entanto, ele e Tom Love também reconheceram que a compatibilidade com versões anteriores com C era extremamente importante no meio de engenharia de telecomunicações da ITT.

Cox começou a escrever um pré-processador para C para adicionar algumas das habilidades de Smalltalk. Ele logo teve uma implementação funcional de uma extensão orientada a objetos para a linguagem C , que chamou de "OOPC" para Pré-compilador orientado a objetos. Love foi contratado pela Schlumberger Research em 1982 e teve a oportunidade de adquirir a primeira cópia comercial do Smalltalk-80, o que influenciou ainda mais o desenvolvimento de sua criação. Para demonstrar que um progresso real poderia ser feito, Cox mostrou que fazer componentes de software intercambiáveis realmente precisava de apenas algumas mudanças práticas nas ferramentas existentes. Especificamente, eles precisavam oferecer suporte a objetos de maneira flexível, vêm fornecidos com um conjunto de bibliotecas utilizáveis ​​e permitem que o código (e quaisquer recursos necessários ao código) sejam agrupados em um formato de plataforma cruzada.

Love e Cox eventualmente formaram a PPI para comercializar seu produto, que combinava um compilador Objective-C com bibliotecas de classes. Em 1986, Cox publicou a descrição principal de Objective-C em sua forma original no livro Object-Oriented Programming, An Evolutionary Approach . Embora ele tenha tido o cuidado de apontar que há mais no problema de reutilização do que apenas o que Objective-C fornece, a linguagem frequentemente se vê comparando recurso por recurso com outras linguagens.

Popularização através da NeXT

Em 1988, a NeXT licenciou Objective-C de StepStone (o novo nome de PPI, o proprietário da marca registrada Objective-C) e estendeu o compilador GCC para suportar Objective-C. A NeXT desenvolveu as bibliotecas AppKit e Foundation Kit nas quais a interface de usuário NeXTSTEP e o Interface Builder foram baseados. Embora as estações de trabalho NeXT não tenham causado um grande impacto no mercado, as ferramentas foram amplamente elogiadas na indústria. Isso levou a NeXT a abandonar a produção de hardware e se concentrar em ferramentas de software, vendendo NeXTSTEP (e OPENSTEP) como uma plataforma para programação personalizada.

Para contornar os termos da GPL , a NeXT tinha a intenção original de enviar o frontend Objective-C separadamente, permitindo ao usuário vinculá-lo ao GCC para produzir o executável do compilador. Embora inicialmente aceito por Richard M. Stallman , este plano foi rejeitado depois que Stallman consultou os advogados do GNU e a NeXT concordou em tornar Objective-C parte do GCC.

O trabalho para estender o GCC foi liderado por Steve Naroff, que se juntou à NeXT vindo da StepStone. As alterações do compilador foram disponibilizadas de acordo com os termos da licença GPL , mas as bibliotecas de tempo de execução não, tornando a contribuição de código aberto inutilizável para o público em geral. Isso levou outras partes a desenvolverem tais bibliotecas de tempo de execução sob licença de código aberto. Mais tarde, Steve Naroff também foi o principal contribuidor para trabalhar na Apple para construir o frontend Objective-C para o Clang .

O projeto GNU começou a trabalhar na implementação do software livre Cocoa , denominado GNUstep , baseado no padrão OpenStep . Dennis Glatting escreveu o primeiro runtime GNU Objective-C em 1992. O runtime GNU Objective-C, que está em uso desde 1993, é o desenvolvido por Kresten Krab Thorup quando ele era um estudante universitário na Dinamarca . Thorup também trabalhou na NeXT de 1993 a 1996.

Desenvolvimento Apple e Swift

Depois de adquirir a NeXT em 1996, a Apple Computer usado OpenStep em seu então novo sistema operacional, Mac OS X . Isso incluiu Objective-C, a ferramenta de desenvolvedor baseada em Objective-C da NeXT, Project Builder , e sua ferramenta de design de interface, Interface Builder . Ambos foram posteriormente fundidos em um único aplicativo, o Xcode . A maior parte da API Cocoa atual da Apple é baseada em objetos de interface OpenStep e é o ambiente Objective-C mais significativo usado para desenvolvimento ativo.

Na WWDC 2014, a Apple apresentou uma nova linguagem, Swift , que foi caracterizada como "Objective-C sem o C".

Sintaxe

Objective-C é uma camada fina sobre C e é um " superconjunto estrito " de C, o que significa que é possível compilar qualquer programa C com um compilador Objective-C e incluir livremente o código da linguagem C em uma classe Objective-C.

Objective-C deriva sua sintaxe de objeto de Smalltalk . Toda a sintaxe para operações não orientadas a objetos (incluindo variáveis ​​primitivas, pré-processamento, expressões, declarações de funções e chamadas de funções) são idênticas às de C, enquanto a sintaxe para recursos orientados a objetos é uma implementação de Smalltalk- estilo de mensagens.

Mensagens

O modelo Objective-C de programação orientada a objetos é baseado na passagem de mensagens para instâncias de objetos. Em Objective-C não se chama um método ; um envia uma mensagem . Isso é diferente do modelo de programação no estilo Simula usado pelo C ++ . A diferença entre esses dois conceitos está em como o código referenciado pelo método ou nome da mensagem é executado. Em uma linguagem de estilo Simula, o nome do método é, na maioria dos casos, vinculado a uma seção de código na classe de destino pelo compilador. Em Smalltalk e Objective-C, o destino de uma mensagem é resolvido em tempo de execução, com o próprio objeto receptor interpretando a mensagem. Um método é identificado por um seletor ou SEL - um identificador único para cada nome de mensagem, geralmente apenas uma string terminada em NUL representando seu nome - e resolvido para um ponteiro de método C que o implementa: um IMP . Uma consequência disso é que o sistema de passagem de mensagens não tem verificação de tipo. Não é garantido que o objeto para o qual a mensagem é direcionada - o receptor - responda a uma mensagem e, caso não responda, gera uma exceção.

O envio do método de mensagem para o objeto apontado pelo ponteiro obj exigiria o seguinte código em C ++ :

obj->method(argument);

Em Objective-C, isso é escrito da seguinte forma:

[obj method:argument];

A chamada do "método" é traduzida pelo compilador para a família objc_msgSend (id self, SEL op, ...) de funções de tempo de execução. Diferentes implementações lidam com adições modernas, como super . Em famílias GNU, esta função é denominada objc_msg_sendv , mas foi descontinuada em favor de um sistema de pesquisa moderno em objc_msg_lookup .

Ambos os estilos de programação têm seus pontos fortes e fracos. A programação orientada a objetos no estilo Simula ( C ++ ) permite herança múltipla e execução mais rápida usando vinculação em tempo de compilação sempre que possível, mas não oferece suporte a vinculação dinâmica por padrão. Ele também força todos os métodos a terem uma implementação correspondente, a menos que sejam abstratos . A programação do estilo Smalltalk, conforme usada em Objective-C, permite que as mensagens não sejam implementadas, com o método resolvido para sua implementação em tempo de execução. Por exemplo, uma mensagem pode ser enviada a uma coleção de objetos, aos quais apenas alguns responderão, sem medo de produzir erros de tempo de execução. A passagem de mensagens também não requer que um objeto seja definido em tempo de compilação. Uma implementação ainda é necessária para que o método seja chamado no objeto derivado. (Consulte a seção de tipagem dinâmica abaixo para obter mais vantagens da vinculação dinâmica (tardia).)

Interfaces e implementações

Objective-C requer que a interface e a implementação de uma classe estejam em blocos de código declarados separadamente. Por convenção, os desenvolvedores colocam a interface em um arquivo de cabeçalho e a implementação em um arquivo de código. Os arquivos de cabeçalho, normalmente com sufixo .h, são semelhantes aos arquivos de cabeçalho C, enquanto os arquivos de implementação (método), normalmente com sufixo .m, podem ser muito semelhantes aos arquivos de código C.

Interface

Isso é análogo às declarações de classe usadas em outras linguagens orientadas a objetos, como C ++ ou Python.

A interface de uma classe geralmente é definida em um arquivo de cabeçalho. Uma convenção comum é nomear o arquivo de cabeçalho após o nome da classe, por exemplo, Ball.h conteria a interface para a classe Ball .

An interface declaration takes the form:

@interface classname : superclassname {
  // instance variables
}
+ classMethod1;
+ (return_type)classMethod2;
+ (return_type)classMethod3:(param1_type)param1_varName;

- (return_type)instanceMethod1With1Parameter:(param1_type)param1_varName;
- (return_type)instanceMethod2With2Parameters:(param1_type)param1_varName
                              param2_callName:(param2_type)param2_varName;
@end

Acima, os sinais de mais denotam métodos de classe ou métodos que podem ser chamados na própria classe (não em uma instância), e os sinais de menos denotam métodos de instância , que só podem ser chamados em uma instância particular da classe. Os métodos de classe também não têm acesso a variáveis ​​de instância .

O código acima é aproximadamente equivalente à seguinte interface C ++ :

class classname : public superclassname {
protected:
  // instance variables

public:
  // Class (static) functions
  static void *classMethod1();
  static return_type classMethod2();
  static return_type classMethod3(param1_type param1_varName);

  // Instance (member) functions
  return_type instanceMethod1With1Parameter(param1_type param1_varName);
  return_type
  instanceMethod2With2Parameters(param1_type param1_varName,
                                 param2_type param2_varName = default);
};

Observe que instanceMethod2With2Parameters: param2_callName: demonstra a intercalação de segmentos do seletor com expressões de argumento, para as quais não há equivalente direto em C / C ++.

Os tipos de retorno podem ser qualquer tipo C padrão , um ponteiro para um objeto Objective-C genérico, um ponteiro para um tipo específico de objeto, como NSArray *, NSImage * ou NSString *, ou um ponteiro para a classe à qual o método pertence (tipo de instância). O tipo de retorno padrão é o id de tipo Objective-C genérico .

Os argumentos do método começam com um nome que rotula o argumento que faz parte do nome do método, seguido por dois pontos seguido pelo tipo de argumento esperado entre parênteses e o nome do argumento. O rótulo pode ser omitido.

- (void)setRangeStart:(int)start end:(int)end;
- (void)importDocumentWithName:(NSString *)name
      withSpecifiedPreferences:(Preferences *)prefs
                    beforePage:(int)insertPage;

Um derivado da definição da interface é a categoria , que permite adicionar métodos às classes existentes.

Implementação

A interface declara apenas a interface da classe e não os métodos em si: o código real é escrito no arquivo de implementação. Os arquivos de implementação (método) normalmente têm a extensão do arquivo .m, que originalmente significava "mensagens".

@implementation classname
+ (return_type)classMethod {
  // implementation
}
- (return_type)instanceMethod {
  // implementation
}
@end

Os métodos são escritos usando suas declarações de interface. Comparando Objective-C e C:

- (int)method:(int)i {
  return [self square_root:i];
}
int function(int i) {
  return square_root(i);
}

A sintaxe permite pseudo- nomenclatura de argumentos .

- (void)changeColorToRed:(float)red green:(float)green blue:(float)blue {
  //... Implementation ...
}

// Called like so:
[myColor changeColorToRed:5.0 green:2.0 blue:6.0];

As representações internas de um método variam entre as diferentes implementações de Objective-C. Se myColor for da classe Color , o método de instância -changeColorToRed: green: blue: pode ser internamente rotulado como _i_Color_changeColorToRed_green_blue . O i é para se referir a um método de instância, com a classe e os nomes dos métodos anexados e dois pontos alterados para sublinhados. Como a ordem dos parâmetros faz parte do nome do método, ela não pode ser alterada para se adequar ao estilo ou expressão de codificação, como acontece com os parâmetros nomeados verdadeiros.

No entanto, os nomes internos da função raramente são usados ​​diretamente. Geralmente, as mensagens são convertidas em chamadas de função definidas na biblioteca de tempo de execução Objective-C. Não é necessariamente conhecido no momento do link qual método será chamado porque a classe do receptor (o objeto que está sendo enviado a mensagem) não precisa ser conhecida até o tempo de execução.

Instanciação

Depois que uma classe Objective-C é escrita, ela pode ser instanciada. Isso é feito primeiro alocando uma instância não inicializada da classe (um objeto) e, em seguida, inicializando-a. Um objeto não está totalmente funcional até que ambas as etapas sejam concluídas. Essas etapas devem ser realizadas com uma linha de código para que nunca haja um objeto alocado que não tenha passado pela inicialização (e porque não é aconselhável manter o resultado intermediário, pois -initpode retornar um objeto diferente daquele no qual é chamado).

Instanciação com o inicializador padrão sem parâmetros:

MyObject *foo = [[MyObject alloc] init];

Instanciação com um inicializador personalizado:

MyObject *foo = [[MyObject alloc] initWithString:myString];

No caso em que nenhuma inicialização personalizada está sendo realizada, o método "novo" pode frequentemente ser usado no lugar das mensagens de alloc-init:

MyObject *foo = [MyObject new];

Além disso, algumas classes implementam inicializadores de método de classe. Como +new, eles combinam +alloce -init, ao contrário +new, eles retornam uma instância autoreleased. Alguns inicializadores de método de classe usam parâmetros:

MyObject *foo = [MyObject object];
MyObject *bar = [MyObject objectWithString:@"Wikipedia :)"];

O alloc aloca memória mensagem o suficiente para manter todas as variáveis de instância para um objeto, define todas as variáveis de instância para valores zero, e transforma a memória em uma instância da classe; em nenhum momento durante a inicialização a memória é uma instância da superclasse.

A mensagem init executa a configuração da instância na criação. O método init geralmente é escrito da seguinte forma:

- (id)init {
    self = [super init];
    if (self) {
        // perform initialization of object here
    }
    return self;
}

No exemplo acima, observe o idtipo de retorno. Este tipo significa "ponteiro para qualquer objeto" em Objective-C (consulte a seção sobre tipagem dinâmica ).

O padrão inicializador é usado para garantir que o objeto seja inicializado corretamente por sua superclasse antes que o método init execute sua inicialização. Ele executa as seguintes ações:

  1. self = [super init]
    Envia à instância da superclasse uma mensagem init e atribui o resultado a self (ponteiro para o objeto atual).
  2. if (self)
    Verifica se o ponteiro do objeto retornado é válido antes de realizar qualquer inicialização.
  3. retornar a si mesmo
    Retorna o valor de self para o chamador.

Um ponteiro de objeto inválido tem o valor nil ; instruções condicionais como "if" tratam nil como um ponteiro nulo, então o código de inicialização não será executado se [super init] retornou nil. Se houver um erro na inicialização, o método init deve realizar qualquer limpeza necessária, incluindo o envio de uma mensagem de "liberação" para si mesmo e retornar nil para indicar que a inicialização falhou. Qualquer verificação de tais erros deve ser realizada somente após ter chamado a inicialização da superclasse para garantir que a destruição do objeto seja feita corretamente.

Se uma classe tem mais de um método de inicialização, apenas um deles (o "inicializador designado") precisa seguir esse padrão; outros devem chamar o inicializador designado em vez do inicializador da superclasse.

Protocolos

Em outras linguagens de programação, eles são chamados de "interfaces".

Objective-C foi estendido na NeXT para introduzir o conceito de herança múltipla de especificação, mas não implementação, por meio da introdução de protocolos . Este é um padrão alcançável como uma classe base herdada múltipla abstrata em C ++ ou como uma "interface" (como em Java e C # ). Objective-C faz uso de protocolos ad hoc chamados protocolos informais e protocolos reforçados pelo compilador chamados protocolos formais .

Um protocolo informal é uma lista de métodos que uma classe pode optar por implementar. É especificado na documentação, uma vez que não tem presença no idioma. Os protocolos informais são implementados como uma categoria (veja abaixo) no NSObject e geralmente incluem métodos opcionais que, se implementados, podem mudar o comportamento de uma classe. Por exemplo, uma classe de campo de texto pode ter um delegado que implementa um protocolo informal com um método opcional para realizar o preenchimento automático de texto digitado pelo usuário. O campo de texto descobre se o delegado implementa esse método (por meio de reflexão ) e, em caso afirmativo, chama o método do delegado para oferecer suporte ao recurso autocompletar.

Um protocolo formal é semelhante a uma interface em Java, C # e Ada 2005 . É uma lista de métodos que qualquer classe pode se declarar para implementar. Versões de Objective-C anteriores a 2.0 exigiam que uma classe implementasse todos os métodos em um protocolo que ela se declara adotando; o compilador emitirá um erro se a classe não implementar todos os métodos de seus protocolos declarados. Objective-C 2.0 adicionou suporte para marcar certos métodos em um protocolo opcional, e o compilador não forçará a implementação de métodos opcionais.

Uma classe deve ser declarada para implementar aquele protocolo para ser considerada em conformidade com ele. Isso é detectável em tempo de execução. Os protocolos formais não podem fornecer nenhuma implementação; eles simplesmente garantem aos chamadores que as classes que estão em conformidade com o protocolo fornecerão implementações. Na biblioteca NeXT / Apple, os protocolos são freqüentemente usados ​​pelo sistema de Objetos Distribuídos para representar as habilidades de um objeto em execução em um sistema remoto.

A sintaxe

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end

denota que existe a ideia abstrata de bloqueio. Ao declarar na definição de classe que o protocolo é implementado,

@interface NSLock : NSObject <NSLocking>
// ...
@end

instâncias de NSLock afirmam que eles fornecerão uma implementação para os dois métodos de instância.

Digitação Dinâmica

Objective-C, como Smalltalk, pode usar tipagem dinâmica : um objeto pode receber uma mensagem que não está especificada em sua interface. Isso pode permitir maior flexibilidade, pois permite que um objeto "capture" uma mensagem e envie a mensagem para um objeto diferente que pode responder à mensagem apropriadamente ou, da mesma forma, envie a mensagem para outro objeto. Esse comportamento é conhecido como encaminhamento ou delegação de mensagens (veja abaixo). Como alternativa, um manipulador de erros pode ser usado caso a mensagem não possa ser encaminhada. Se um objeto não encaminhar uma mensagem, responder a ela ou tratar um erro, o sistema gerará uma exceção de tempo de execução. Se as mensagens forem enviadas para nil (o ponteiro do objeto nulo), elas serão ignoradas silenciosamente ou gerarão uma exceção genérica, dependendo das opções do compilador.

As informações de digitação estática também podem ser adicionadas opcionalmente às variáveis. Esta informação é então verificada em tempo de compilação. Nas quatro instruções a seguir, informações de tipo cada vez mais específicas são fornecidas. As instruções são equivalentes em tempo de execução, mas as informações extras permitem que o compilador avise o programador se o argumento passado não corresponder ao tipo especificado.

- (void)setMyValue:(id)foo;

Na declaração acima, foo pode ser de qualquer classe.

- (void)setMyValue:(id<NSCopying>)foo;

Na declaração acima, foo pode ser uma instância de qualquer classe que esteja em conformidade com o NSCopyingprotocolo.

- (void)setMyValue:(NSNumber *)foo;

Na instrução acima, foo deve ser uma instância da classe NSNumber .

- (void)setMyValue:(NSNumber<NSCopying> *)foo;

Na instrução acima, foo deve ser uma instância da classe NSNumber e deve estar em conformidade com o NSCopyingprotocolo.

Em Objective-C, todos os objetos são representados como ponteiros e a inicialização estática não é permitida. O objeto mais simples é o tipo para o qual id ( objc_obj * ) aponta, que só tem um ponteiro isa descrevendo sua classe. Outros tipos de C, como valores e estruturas, permanecem inalterados porque não fazem parte do sistema de objetos. Essa decisão difere do modelo de objeto C ++, onde estruturas e classes são unidas.

Encaminhamento

Objective-C permite o envio de uma mensagem a um objeto que pode não responder. Em vez de responder ou simplesmente deixar cair a mensagem, um objeto pode encaminhar a mensagem para um objeto que pode responder. O encaminhamento pode ser usado para simplificar a implementação de certos padrões de design , como o padrão de observador ou o padrão de proxy .

O tempo de execução Objective-C especifica um par de métodos em Object

  • métodos de encaminhamento:
    - (retval_t)forward:(SEL)sel args:(arglist_t)args; // with GCC
    - (id)forward:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
    
  • métodos de ação:
    - (retval_t)performv:(SEL)sel args:(arglist_t)args; // with GCC
    - (id)performv:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
    

Um objeto que deseja implementar o encaminhamento precisa apenas substituir o método de encaminhamento por um novo método para definir o comportamento de encaminhamento. O método de ação performv :: não precisa ser sobrescrito, pois este método apenas executa uma ação com base no seletor e nos argumentos. Observe o SELtipo, que é o tipo de mensagens em Objective-C.

Nota: em OpenStep, Cocoa e GNUstep, os frameworks comumente usados ​​de Objective-C, não se usa a classe Object . O método - (void) forwardInvocation: (NSInvocation *) anInvocation da classe NSObject é usado para fazer o encaminhamento.

Exemplo

Aqui está um exemplo de um programa que demonstra os fundamentos do encaminhamento.

Forwarder.h
#import <objc/Object.h>

@interface Forwarder : Object {
  id recipient; // The object we want to forward the message to.
}

// Accessor methods.
- (id)recipient;
- (id)setRecipient:(id)_recipient;
@end
Forwarder.m
#import "Forwarder.h"

@implementation Forwarder
- (retval_t)forward:(SEL)sel args:(arglist_t)args {
  /*
  * Check whether the recipient actually responds to the message.
  * This may or may not be desirable, for example, if a recipient
  * in turn does not respond to the message, it might do forwarding
  * itself.
  */
  if ([recipient respondsToSelector:sel]) {
    return [recipient performv:sel args:args];
  } else {
    return [self error:"Recipient does not respond"];
  }
}

- (id)setRecipient:(id)_recipient {
  [recipient autorelease];
  recipient = [_recipient retain];
  return self;
}

- (id)recipient {
  return recipient;
}
@end
Recipient.h
#import <objc/Object.h>

// A simple Recipient object.
@interface Recipient : Object
- (id)hello;
@end
Recipient.m
#import "Recipient.h"

@implementation Recipient

- (id)hello {
  printf("Recipient says hello!\n");

  return self;
}

@end
main.m
#import "Forwarder.h"
#import "Recipient.h"

int main(void) {
  Forwarder *forwarder = [Forwarder new];
  Recipient *recipient = [Recipient new];

  [forwarder setRecipient:recipient]; // Set the recipient.
  /*
  * Observe forwarder does not respond to a hello message! It will
  * be forwarded. All unrecognized methods will be forwarded to
  * the recipient
  * (if the recipient responds to them, as written in the Forwarder)
  */
  [forwarder hello];

  [recipient release];
  [forwarder release];

  return 0;
}

Notas

Quando compilado usando gcc , o compilador relata:

$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc
main.m: In function `main':
main.m:12: warning: `Forwarder' does not respond to `hello'
$

O compilador está relatando o ponto feito anteriormente, que o Forwarder não responde às mensagens de olá. Nessa circunstância, é seguro ignorar o aviso, pois o encaminhamento foi implementado. A execução do programa produz esta saída:

$ ./a.out
Recipient says hello!

Categorias

Durante o design do Objective-C, uma das principais preocupações era a manutenção de grandes bases de código. A experiência do mundo da programação estruturada mostrou que uma das principais maneiras de melhorar o código era dividi-lo em pedaços menores. Objective-C emprestou e estendeu o conceito de categorias de implementações Smalltalk para ajudar neste processo.

Além disso, os métodos dentro de uma categoria são adicionados a uma classe em tempo de execução . Assim, as categorias permitem ao programador adicionar métodos a uma classe existente - uma classe aberta - sem a necessidade de recompilar essa classe ou mesmo ter acesso ao seu código-fonte. Por exemplo, se um sistema não contém um verificador ortográfico em sua implementação de String, ele pode ser adicionado sem modificar o código-fonte da String.

Os métodos dentro das categorias tornam-se indistinguíveis dos métodos de uma classe quando o programa é executado. Uma categoria tem acesso total a todas as variáveis ​​de instância dentro da classe, incluindo variáveis ​​privadas.

Se uma categoria declara um método com a mesma assinatura de método de um método existente em uma classe, o método da categoria é adotado. Assim, as categorias podem não apenas adicionar métodos a uma classe, mas também substituir os métodos existentes. Este recurso pode ser usado para corrigir bugs em outras classes reescrevendo seus métodos ou para causar uma mudança global no comportamento de uma classe dentro de um programa. Se duas categorias têm métodos com o mesmo nome, mas assinaturas de método diferentes, é indefinido qual método de categoria é adotado.

Outros idiomas tentaram adicionar esse recurso de várias maneiras. O TOM levou o sistema Objective-C um passo adiante e também permitiu a adição de variáveis. Em vez disso, outras linguagens usaram soluções baseadas em protótipos , sendo a mais notável o Self .

As linguagens C # e Visual Basic.NET implementam funcionalidade superficialmente semelhante na forma de métodos de extensão , mas estes não têm acesso às variáveis ​​privadas da classe. Ruby e várias outras linguagens de programação dinâmica referem-se à técnica como " patching do macaco ".

Logtalk implementa um conceito de categorias (como entidades de primeira classe) que inclui a funcionalidade de categorias Objective-C (categorias Logtalk também podem ser usadas como unidades refinadas de composição ao definir, por exemplo, novas classes ou protótipos; em particular, uma categoria Logtalk pode ser importado virtualmente por qualquer número de classes e protótipos).

Exemplo de uso de categorias

Este exemplo constrói uma classe Integer , definindo primeiro uma classe básica com apenas métodos acessadores implementados e adicionando duas categorias, Aritmética e Exibição , que estendem a classe básica. Embora as categorias possam acessar os membros de dados privados da classe base, geralmente é uma boa prática acessar esses membros de dados privados por meio dos métodos do acessador, o que ajuda a manter as categorias mais independentes da classe base. A implementação de tais acessores é um uso típico de categorias. Outra é usar categorias para adicionar métodos à classe base. No entanto, não é considerado uma boa prática usar categorias para sobrescrever subclasses, também conhecido como monkey patching . Os protocolos informais são implementados como uma categoria na classe base NSObject . Por convenção, os arquivos que contêm categorias que estendem as classes básicas terão o nome BaseClass + ExtensionClass.h .

Integer.h
#import <objc/Object.h>

@interface Integer : Object {
  int integer;
}

- (int)integer;
- (id)integer:(int)_integer;
@end
Integer.m
#import "Integer.h"

@implementation Integer
- (int) integer {
  return integer;
}

- (id) integer: (int) _integer {
  integer = _integer;
  return self;
}
@end
Inteiro + Aritmética.h
#import "Integer.h"

@interface Integer (Arithmetic)
- (id) add: (Integer *) addend;
- (id) sub: (Integer *) subtrahend;
@end
Inteiro + Aritmética.m
# import "Integer+Arithmetic.h"

@implementation Integer (Arithmetic)
- (id) add: (Integer *) addend {
  return [self integer: [self integer] + [addend integer]];
}

- (id) sub: (Integer *) subtrahend {
  return [self integer: [self integer] - [subtrahend integer]];
}
@end
Integer + Display.h
#import "Integer.h"

@interface Integer (Display)
- (id) showstars;
- (id) showint;
@end
Integer + Display.m
# import "Integer+Display.h"

@implementation Integer (Display)
- (id) showstars {
  int i, x = [self integer];
  for (i = 0; i < x; i++) {
    printf("*");
  }
  printf("\n");

  return self;
}

- (id) showint {
  printf("%d\n", [self integer]);

  return self;
}
@end
main.m
#import "Integer.h"
#import "Integer+Arithmetic.h"
#import "Integer+Display.h"

int main(void) {
  Integer *num1 = [Integer new], *num2 = [Integer new];
  int x;

  printf("Enter an integer: ");
  scanf("%d", &x);

  [num1 integer:x];
  [num1 showstars];

  printf("Enter an integer: ");
  scanf("%d", &x);

  [num2 integer:x];
  [num2 showstars];

  [num1 add:num2];
  [num1 showint];

  return 0;
}

Notas

A compilação é realizada, por exemplo, por:

gcc -x objective-c main.m Integer.m Integer+Arithmetic.m Integer+Display.m -lobjc

Pode-se experimentar deixando de fora as linhas #import "Integer + Arithmetic.h" e [num1 add: num2] e omitindo Integer + Arithmetic.m na compilação. O programa ainda será executado. Isso significa que é possível misturar e combinar categorias adicionadas, se necessário; se uma categoria não precisa ter alguma habilidade, ela simplesmente não pode ser compilada.

Posando

Objective-C permite que uma classe substitua completamente outra classe dentro de um programa. Diz-se que a classe substituta "se faz passar por" a classe-alvo.

A pose de aula foi declarada obsoleta com o Mac OS X v10.5 e não está disponível no tempo de execução de 64 bits. Funcionalidades semelhantes podem ser alcançadas usando método swizzling em categorias, que troca a implementação de um método por outro que tenha a mesma assinatura.

Para as versões que ainda suportam poses, todas as mensagens enviadas para a classe de destino são, em vez disso, recebidas pela classe de poses. Existem várias restrições:

  • Uma classe só pode se passar por uma de suas superclasses diretas ou indiretas.
  • A classe de posicionamento não deve definir nenhuma nova variável de instância que esteja ausente da classe de destino (embora possa definir ou substituir métodos).
  • A classe alvo pode não ter recebido nenhuma mensagem antes da apresentação.

Posar, da mesma forma com as categorias, permite o aumento global das classes existentes. Posar permite dois recursos ausentes das categorias:

  • Uma classe de apresentação pode chamar métodos substituídos por meio de super, incorporando assim a implementação da classe de destino.
  • Uma classe de poses pode substituir métodos definidos em categorias.

Por exemplo,

@interface CustomNSApplication : NSApplication
@end

@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu {
  // do something with menu
}
@end

class_poseAs ([CustomNSApplication class], [NSApplication class]);

Isso intercepta cada invocação de setMainMenu para NSApplication.

#importar

Na linguagem C, a #includediretiva de pré-compilação sempre faz com que o conteúdo de um arquivo seja inserido na fonte naquele ponto. Objective-C tem a #importdiretiva, equivalente, exceto que cada arquivo é incluído apenas uma vez por unidade de compilação, evitando a necessidade de incluir guardas .

Compilação Linux gcc

// FILE: hello.m
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
    /* my first program in Objective-C */
    NSLog(@"Hello, World! \n");
    return 0;
}
# Compile Command Line for gcc and MinGW Compiler:
$ gcc \
    $(gnustep-config --objc-flags) \
    -o hello \
    hello.m \
    -L /GNUstep/System/Library/Libraries \
    -lobjc \
    -lgnustep-base

$ ./hello

Outras características

Os recursos do Objective-C geralmente permitem soluções flexíveis e geralmente fáceis para problemas de programação.

  • A delegação de métodos a outros objetos e a invocação remota podem ser facilmente implementadas usando categorias e encaminhamento de mensagens.
  • O movimento do ponteiro isa permite que as classes mudem em tempo de execução. Normalmente usado para depuração, onde objetos liberados são transformados em objetos zumbis cujo único propósito é relatar um erro quando alguém os chama. Swizzling também foi usado no Enterprise Objects Framework para criar falhas de banco de dados. O Swizzling é usado hoje pelo Framework da Apple para implementar a observação de valores-chave .

Variantes de linguagem

Objective-C ++

Objective-C ++ é uma variante de linguagem aceita pelo front-end para GNU Compiler Collection e Clang , que pode compilar arquivos de origem que usam uma combinação de sintaxe C ++ e Objective-C. Objective-C ++ adiciona a C ++ as extensões que Objective-C adiciona a C. Como nada é feito para unificar a semântica por trás dos vários recursos da linguagem, certas restrições se aplicam:

  • Uma classe C ++ não pode derivar de uma classe Objective-C e vice-versa.
  • Os namespaces C ++ não podem ser declarados dentro de uma declaração Objective-C.
  • As declarações Objective-C podem aparecer apenas no escopo global, não dentro de um namespace C ++
  • As classes Objective-C não podem ter variáveis ​​de instância de classes C ++ que não tenham um construtor padrão ou que tenham um ou mais métodos virtuais , mas ponteiros para objetos C ++ podem ser usados ​​como variáveis ​​de instância sem restrição (aloque-os com new no método -init).
  • A semântica "por valor" do C ++ não pode ser aplicada a objetos Objective-C, que só são acessíveis por meio de ponteiros.
  • Uma declaração Objective-C não pode estar dentro de uma declaração de modelo C ++ e vice-versa. No entanto, os tipos Objective-C (por exemplo, Classname *) podem ser usados ​​como parâmetros de modelo C ++.
  • O tratamento de exceções Objective-C e C ++ é distinto; os manipuladores de cada um não podem manipular exceções do outro tipo. Como resultado, os destruidores de objetos não são executados. Isso foi atenuado nos tempos de execução "Objective-C 2.0" recentes, pois as exceções Objective-C foram substituídas por exceções C ++ completamente (tempo de execução Apple) ou parcialmente quando a biblioteca Objective-C ++ está vinculada (GNUstep libobjc2).
  • Blocos Objective-C e lambdas C ++ 11 são entidades distintas. No entanto, um bloco é gerado de forma transparente no macOS ao passar um lambda onde um bloco é esperado.

Objective-C 2.0

Na Conferência Mundial de Desenvolvedores de 2006 , a Apple anunciou o lançamento de "Objective-C 2.0", uma revisão da linguagem Objective-C para incluir "coleta de lixo moderna, aprimoramentos de sintaxe, aprimoramentos de desempenho de tempo de execução e suporte a 64 bits". O Mac OS X v10.5 , lançado em outubro de 2007, incluía um compilador Objective-C 2.0. O GCC 4.6 suporta muitos novos recursos Objective-C, como propriedades declaradas e sintetizadas, sintaxe de ponto, enumeração rápida, métodos de protocolo opcionais, atributos de método / protocolo / classe, extensões de classe e uma nova API GNU Objective-C em tempo de execução.

A nomenclatura Objective-C 2.0 representa uma quebra no sistema de versionamento da linguagem, já que a última versão do Objective-C para NeXT foi "objc4". O nome do projeto foi mantido na última versão do código-fonte do tempo de execução Objective-C legado no Mac OS X Leopard (10.5).

Coleta de lixo

Objective-C 2.0 forneceu um coletor de lixo opcional conservador e geracional . Quando executado no modo compatível com versões anteriores , o tempo de execução transforma as operações de contagem de referência , como "reter" e "liberar", em modo autônomo . Todos os objetos estavam sujeitos à coleta de lixo quando a coleta de lixo estava habilitada. Os ponteiros C regulares podem ser qualificados com "__strong" para também acionar as interceptações do compilador de barreira de gravação subjacente e, assim, participar da coleta de lixo. Um subsistema fraco de zeragem também foi fornecido, de modo que os ponteiros marcados como "__weak" são definidos como zero quando o objeto (ou mais simplesmente, a memória GC) é coletado. O coletor de lixo não existe na implementação iOS de Objective-C 2.0. A coleta de lixo em Objective-C é executada em um thread de segundo plano de baixa prioridade e pode ser interrompida em eventos do usuário, com a intenção de manter a experiência do usuário responsiva.

A coleta de lixo foi descontinuada no Mac OS X v10.8 em favor da contagem automática de referência (ARC). Objective-C no iOS 7 em execução no ARM64 usa 19 bits de uma palavra de 64 bits para armazenar a contagem de referência, como uma forma de ponteiros marcados .

Propriedades

Objective-C 2.0 introduz uma nova sintaxe para declarar variáveis ​​de instância como propriedades , com atributos opcionais para configurar a geração de métodos acessadores. Propriedades são, em certo sentido, variáveis ​​de instância públicas; isto é, declarar uma variável de instância como uma propriedade fornece classes externas com acesso (possivelmente limitado, por exemplo, somente leitura) a essa propriedade. Uma propriedade pode ser declarada como "somente leitura" e pode ser fornecida com semântica de armazenamento como assign, copyou retain. Por padrão, as propriedades são consideradas atomic, o que resulta em um bloqueio impedindo que vários encadeamentos os acessem ao mesmo tempo. Uma propriedade pode ser declarada como nonatomic, o que remove esse bloqueio.

@interface Person : NSObject {
@public
  NSString *name;
@private
  int age;
}

@property(copy) NSString *name;
@property(readonly) int age;

- (id)initWithAge:(int)age;
@end

As propriedades são implementadas por meio da @synthesizepalavra - chave, que gera métodos getter (e setter, se não somente leitura) de acordo com a declaração da propriedade. Como alternativa, os métodos getter e setter devem ser implementados explicitamente ou a @dynamicpalavra - chave pode ser usada para indicar que os métodos do acessador serão fornecidos por outros meios. Quando compilado usando o clang 3.1 ou superior, todas as propriedades que não são explicitamente declaradas com @dynamic, marcadas readonlyou possuem getter e setter completos implementados pelo usuário serão automaticamente implicitamente @synthesize'd.

@implementation Person
@synthesize name;

- (id)initWithAge:(int)initAge {
  self = [super init];
  if (self) {
    // NOTE: direct instance variable assignment, not property setter
    age = initAge;
  }
  return self;
}

- (int)age {
  return age;
}
@end

As propriedades podem ser acessadas usando a sintaxe de transmissão de mensagem tradicional, notação de ponto ou, na codificação de valor-chave, por nome por meio dos métodos "valueForKey:" / "setValue: forKey:".

Person *aPerson = [[Person alloc] initWithAge:53];
aPerson.name = @"Steve"; // NOTE: dot notation, uses synthesized setter,
                         // equivalent to [aPerson setName: @"Steve"];
NSLog(@"Access by message (%@), dot notation(%@), property name(% @) and "
       "direct instance variable access(% @) ",
              [aPerson name],
      aPerson.name, [aPerson valueForKey:@"name"], aPerson -> name);

Para usar a notação de ponto para invocar acessadores de propriedade em um método de instância, a palavra-chave "self" deve ser usada:

- (void)introduceMyselfWithProperties:(BOOL)useGetter {
  NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name));
  // NOTE: getter vs. ivar access
}

As propriedades de uma classe ou protocolo podem ser introspectadas dinamicamente .

int i;
int propertyCount = 0;
objc_property_t *propertyList =
    class_copyPropertyList([aPerson class], &propertyCount);

for (i = 0; i < propertyCount; i++) {
  objc_property_t *thisProperty = propertyList + i;
  const char *propertyName = property_getName(*thisProperty);
  NSLog(@"Person has a property: '%s'", propertyName);
}

Variáveis ​​de instância não frágeis

Objective-C 2.0 fornece variáveis ​​de instância não frágeis quando suportadas pelo tempo de execução (ou seja, ao construir código para macOS de 64 bits e todos os iOS). No tempo de execução moderno, uma camada extra de indireção é adicionada ao acesso à variável da instância, permitindo que o vinculador dinâmico ajuste o layout da instância no tempo de execução. Este recurso permite duas melhorias importantes no código Objective-C:

  • Ele elimina o problema da interface binária frágil ; as superclasses podem mudar de tamanho sem afetar a compatibilidade binária.
  • Ele permite que variáveis ​​de instância que fornecem suporte para propriedades sejam sintetizadas em tempo de execução sem que sejam declaradas na interface da classe.

Enumeração rápida

Em vez de usar um objeto NSEnumerator ou índices para iterar por meio de uma coleção, Objective-C 2.0 oferece a sintaxe de enumeração rápida. No Objective-C 2.0, os loops a seguir são funcionalmente equivalentes, mas têm características de desempenho diferentes.

// Using NSEnumerator
NSEnumerator *enumerator = [thePeople objectEnumerator];
Person *p;

while ((p = [enumerator nextObject]) != nil) {
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using indexes
for (int i = 0; i < [thePeople count]; i++) {
  Person *p = [thePeople objectAtIndex:i];
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using fast enumeration
for (Person *p in thePeople) {
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}

A enumeração rápida gera um código mais eficiente do que a enumeração padrão porque as chamadas de método para enumerar sobre os objetos são substituídas pela aritmética de ponteiro usando o protocolo NSFastEnumeration.

Extensões de classe

Uma extensão de classe tem a mesma sintaxe que uma declaração de categoria sem nome de categoria, e os métodos e propriedades declarados nela são adicionados diretamente à classe principal. É usado principalmente como uma alternativa a uma categoria para adicionar métodos a uma classe sem anunciá-los nos cabeçalhos públicos, com a vantagem de que, para extensões de classe, o compilador verifica se todos os métodos declarados de forma privada estão realmente implementados.

Implicações para o desenvolvimento do Cocoa

Todos os aplicativos Objective-C desenvolvidos para macOS que usam as melhorias acima para Objective-C 2.0 são incompatíveis com todos os sistemas operacionais anteriores a 10.5 (Leopard). Como a enumeração rápida não gera exatamente os mesmos binários que a enumeração padrão, seu uso fará com que um aplicativo trave no Mac OS X versão 10.4 ou anterior.

Blocos

Blocks é uma extensão não padrão para Objective-C (e C e C ++ ) que usa sintaxe especial para criar fechamentos . Os blocos são suportados apenas no Mac OS X 10.6 "Snow Leopard" ou posterior, iOS 4 ou posterior e GNUstep com libobjc2 1.7 e compilação com clang 3.1 ou posterior.

#include <stdio.h>
#include <Block.h>
typedef int (^IntBlock)();

IntBlock MakeCounter(int start, int increment) {
  __block int i = start;

  return Block_copy( ^ {
    int ret = i;
    i += increment;
    return ret;
  });

}

int main(void) {
  IntBlock mycounter = MakeCounter(5, 2);
  printf("First call: %d\n", mycounter());
  printf("Second call: %d\n", mycounter());
  printf("Third call: %d\n", mycounter());

  /* because it was copied, it must also be released */
  Block_release(mycounter);

  return 0;
}
/* Output:
  First call: 5
  Second call: 7
  Third call: 9
*/

Objective-C moderno

A Apple adicionou alguns recursos adicionais ao Objective 2.0 ao longo do tempo. As adições se aplicam apenas ao " compilador Apple LLVM ", ou seja, o frontend clang da linguagem. De maneira confusa, o controle de versão usado pela Apple difere daquele do LLVM upstream; consulte as versões do conjunto de ferramentas Xcode § para obter uma tradução para números de versão LLVM de código aberto.

Contagem de referência automática

A contagem automática de referência (ARC) é um recurso de tempo de compilação que elimina a necessidade de os programadores gerenciarem manualmente as contagens de retenção usando retaine release. Ao contrário da coleta de lixo , que ocorre em tempo de execução, o ARC elimina a sobrecarga de um processo separado que gerencia contagens de retenção. O ARC e o gerenciamento de memória manual não são mutuamente exclusivos; os programadores podem continuar a usar código não ARC em projetos habilitados para ARC, desabilitando o ARC para arquivos de código individuais. O Xcode também pode tentar atualizar automaticamente um projeto para ARC.

O ARC foi introduzido no LLVM 3.0. Isso se traduz em Xcode 4.2 (2011), ou Apple LLVM compilador 3.0.

Literais

Os tempos de execução do NeXT e do Apple Obj-C há muito incluem uma forma abreviada de criar novas strings, usando a sintaxe literal @"a new string", ou passar para constantes CoreFoundation kCFBooleanTruee kCFBooleanFalsepara NSNumbercom valores booleanos. O uso desse formato evita que o programador precise usar initWithStringmétodos mais longos ou semelhantes ao fazer certas operações.

Quando se utiliza a Apple LLVM compilador 4.0 (Xcode 4.4) ou posteriores, matrizes, dicionários, e números ( NSArray, NSDictionary, NSNumberclasses) pode também ser criada usando sintaxe literal em vez de métodos. (O compilador Apple LLVM 4.0 é convertido em código aberto LLVM e Clang 3.1.)

Exemplo sem literais:

NSArray *myArray = [NSArray arrayWithObjects:object1,object2,object3,nil];
NSDictionary *myDictionary1 = [NSDictionary dictionaryWithObject:someObject forKey:@"key"];
NSDictionary *myDictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:object1, key1, object2, key2, nil];
NSNumber *myNumber = [NSNumber numberWithInt:myInt];
NSNumber *mySumNumber= [NSNumber numberWithInt:(2 + 3)];
NSNumber *myBoolNumber = [NSNumber numberWithBool:YES];

Exemplo com literais:

NSArray *myArray = @[ object1, object2, object3 ];
NSDictionary *myDictionary1 = @{ @"key" : someObject };
NSDictionary *myDictionary2 = @{ key1: object1, key2: object2 };
NSNumber *myNumber = @(myInt);
NSNumber *mySumNumber = @(2+3);
NSNumber *myBoolNumber = @YES;
NSNumber *myIntegerNumber = @8;

No entanto, diferente dos literais de string , que compilam para constantes no executável, esses literais são compilados para código equivalente às chamadas de método acima. Em particular, sob o gerenciamento de memória contada por referência manualmente, esses objetos são liberados automaticamente, o que requer cuidado adicional quando, por exemplo, usados ​​com variáveis ​​estáticas de função ou outros tipos de globais.

Assinatura

Ao usar o compilador Apple LLVM 4.0 ou posterior, arrays e dicionários ( NSArraye NSDictionaryclasses) podem ser manipulados usando subscrito. A assinatura pode ser usada para recuperar valores de índices (array) ou chaves (dicionário) e, com objetos mutáveis, também pode ser usada para definir objetos para índices ou chaves. No código, o subscrito é representado por colchetes [ ].

Exemplo sem subscrito:

id object1 = [someArray objectAtIndex:0];
id object2 = [someDictionary objectForKey:@"key"];
[someMutableArray replaceObjectAtIndex:0 withObject:object3];
[someMutableDictionary setObject:object4 forKey:@"key"];

Exemplo com subscrito:

id object1 = someArray[0];
id object2 = someDictionary[@"key"];
someMutableArray[0] = object3;
someMutableDictionary[@"key"] = object4;

Sintaxe "Modern" do Objective-C (1997)

Após a compra da NeXT pela Apple, foram feitas tentativas para tornar a linguagem mais aceitável para programadores mais familiarizados com Java do que Smalltalk. Uma dessas tentativas foi introduzir o que foi apelidado de "sintaxe moderna" para Objective-C na época (em oposição à sintaxe "clássica" atual). Não houve mudança no comportamento, esta era apenas uma sintaxe alternativa. Em vez de escrever uma invocação de método como

    object = [[MyClass alloc] init];
    [object firstLabel: param1 secondLabel: param2];

Em vez disso, foi escrito como

    object = (MyClass.alloc).init;
    object.labels ( param1, param2 );

Da mesma forma, as declarações saíram do formulário

    -(void) firstLabel: (int)param1 secondLabel: (int)param2;

para

    -(void) labels ( int param1, int param2 );

Esta sintaxe "moderna" não é mais suportada nos dialetos atuais da linguagem Objective-C.

mulle-objc

O projeto mulle-objc é outra reimplementação do Objective-C. Ele suporta compiladores GCC ou Clang / LLVM como back-ends. Ele diverge de outros tempos de execução em termos de sintaxe, semântica e compatibilidade ABI. Suporta Linux, FreeBSD e Windows.

Compilador de objetos portáteis

Além da implementação GCC / NeXT / Apple , que adicionou várias extensões à implementação Stepstone original , outra implementação Objective-C gratuita e de código aberto chamada Portable Object Compiler também existe. O conjunto de extensões implementadas pelo Compilador de Objetos Portáteis difere da implementação GCC / NeXT / Apple; em particular, inclui blocos do tipo Smalltalk para Objective-C, embora falte protocolos e categorias, dois recursos usados ​​extensivamente no OpenStep e seus derivados e parentes. No geral, POC representa um estágio mais antigo, pré-NeXT na evolução da linguagem, aproximadamente em conformidade com o livro de 1991 de Brad Cox.

Ele também inclui uma biblioteca de tempo de execução chamada ObjectPak, que é baseada na biblioteca ICPak101 original de Cox (que por sua vez deriva da biblioteca de classes Smalltalk-80) e é radicalmente diferente do OpenStep FoundationKit.

GEOS Objective-C

O sistema PC GEOS usava uma linguagem de programação conhecida como GEOS Objective-C ou goc ; apesar da semelhança do nome, as duas línguas são semelhantes apenas no conceito geral e no uso de palavras-chave prefixadas com um sinal @.

Clang

O conjunto de compiladores Clang , parte do projeto LLVM , implementa Objective-C e outras linguagens. Depois que o GCC 4.3 (2008) mudou para GPLv3, a Apple o abandonou em favor do clang, um compilador que tem mais poder legal para modificar. Como resultado, muitos dos recursos modernos da linguagem Objective-C são suportados apenas pelo Clang.

O esquema de versão da Apple para seu "compilador LLVM" baseado em clang difere da versão de código aberto do LLVM. Consulte as versões do conjunto de ferramentas Xcode § para obter uma tradução

GNU, GNUstep e WinObjC

O projeto GNU está, há muito tempo, interessado em uma plataforma para portar programas NeXT e Obj-C. O ChangeLog para o diretório libobjc no GCC sugere que ele existia antes de 1998 (GCC 2.95), e seu README aponta ainda para uma reescrita em 1993 (GCC 2.4).

O código-fonte do frontend NeXT foi lançado, uma vez que foi feito como parte do GCC, lançou a GNU Public License que força aqueles que fazem trabalhos derivados a fazê-lo. A Apple continuou esta tradição ao lançar seu fork do GCC até 4.2.1, após o qual abandonou o compilador. Os mantenedores do GCC aceitaram as mudanças, mas não investiram muito no suporte a recursos mais novos, como a linguagem Objective-C 2.0.

Os desenvolvedores do GNUstep, interessados ​​na nova linguagem, transformaram o GCC libobjc em um projeto independente do GCC chamado libobjc2 em 2009. Eles também organizaram o tempo de execução para ser usado com o Clang para aproveitar as vantagens da nova sintaxe da linguagem. O GCC mudou lentamente ao mesmo tempo, mas no GCC 4.6.0 (2011) eles mudaram para o Objective-C 2.0 em sua libobjc também. A documentação do GNUstep sugere que a implementação do GCC ainda carece de suporte para blocos, variáveis ​​não frágeis e o ARC mais recente.

A Microsoft bifurcou libobjc2 em uma parte do WinObjC , a ponte iOS para a Plataforma Universal do Windows , em 2015. Combinado com sua própria implementação de Cocoa Touch e APIs subjacentes, o projeto permite a reutilização de código de aplicativo iOS dentro de aplicativos UWP.

No Windows, as ferramentas de desenvolvimento Objective-C são fornecidas para download no site da GNUStep. O GNUStep Development System consiste nos seguintes pacotes: GNUstep MSYS System, GNUstep Core, GNUstep Devel, GNUstep Cairo, ProjectCenter IDE (como o Xcode, mas não tão complexo), Gorm (construtor de interface como o Xcode NIB builder). Esses instaladores binários não são atualizados desde 2016, então pode ser uma ideia melhor apenas instalar compilando no Cygwin ou MSYS2 .

Uso da biblioteca

Objective-C hoje é freqüentemente usado em conjunto com uma biblioteca fixa de objetos padrão (freqüentemente conhecida como um "kit" ou "estrutura"), como Cocoa , GNUstep ou ObjFW . Essas bibliotecas geralmente vêm com o sistema operacional: as bibliotecas GNUstep geralmente vêm com distribuições baseadas em Linux e Cocoa vem com macOS. O programador não é forçado a herdar a funcionalidade da classe base existente (NSObject / OFObject). Objective-C permite a declaração de novas classes raiz que não herdam nenhuma funcionalidade existente. Originalmente, os ambientes de programação baseados em Objective-C normalmente ofereciam uma classe Object como a classe base da qual quase todas as outras classes herdavam. Com a introdução do OpenStep, NeXT criou uma nova classe base chamada NSObject, que ofereceu recursos adicionais sobre Object (uma ênfase no uso de referências de objeto e contagem de referência em vez de ponteiros brutos, por exemplo). Quase todas as classes em Cocoa herdam de NSObject.

A renomeação não apenas serviu para diferenciar o novo comportamento padrão das classes dentro da API OpenStep, mas permitiu o código que usava Object - a classe base original usada no NeXTSTEP (e, mais ou menos, outras bibliotecas de classes Objective-C) - para coexistem no mesmo tempo de execução com o código que usou NSObject (com algumas limitações). A introdução do prefixo de duas letras também se tornou uma forma simplista de namespaces, que falta no Objective-C. Usar um prefixo para criar um identificador de pacote informal tornou-se um padrão de codificação informal na comunidade Objective-C e continua até hoje.

Mais recentemente, os gerenciadores de pacotes começaram a aparecer, como o CocoaPods , que visa ser tanto um gerenciador de pacotes quanto um repositório de pacotes. Muitos códigos Objective-C de código aberto que foram escritos nos últimos anos podem agora ser instalados usando CocoaPods.

Análise da linguagem

As implementações de Objective-C usam um sistema de tempo de execução thin escrito em C, o que adiciona pouco ao tamanho do aplicativo. Em contraste, a maioria dos sistemas orientados a objetos na época em que foram criados usavam grandes tempos de execução de máquinas virtuais . Os programas escritos em Objective-C tendem a não ser muito maiores do que o tamanho de seu código e das bibliotecas (que geralmente não precisam ser incluídos na distribuição do software), em contraste com os sistemas Smalltalk onde uma grande quantidade de memória foi usado apenas para abrir uma janela. Os aplicativos Objective-C tendem a ser maiores do que os aplicativos C ou C ++ semelhantes porque a tipagem dinâmica Objective-C não permite que os métodos sejam removidos ou alinhados. Uma vez que o programador tem liberdade para delegar, encaminhar chamadas, construir seletores em tempo real e passá-los para o sistema de tempo de execução, o compilador Objective-C não pode presumir que é seguro remover métodos não utilizados ou chamadas sequenciais.

Da mesma forma, a linguagem pode ser implementada em compiladores C existentes (no GCC , primeiro como um pré-processador, depois como um módulo) em vez de um novo compilador. Isso permite que Objective-C aproveite a enorme coleção existente de código C, bibliotecas, ferramentas, etc. As bibliotecas C existentes podem ser agrupadas em invólucros Objective-C para fornecer uma interface de estilo OO. Nesse aspecto, é semelhante à biblioteca GObject e à linguagem Vala , amplamente utilizadas no desenvolvimento de aplicações GTK .

Todas essas mudanças práticas reduziram a barreira de entrada , provavelmente o maior problema para a aceitação generalizada de Smalltalk na década de 1980.

Uma crítica comum é que Objective-C não tem suporte de linguagem para namespaces . Em vez disso, os programadores são forçados a adicionar prefixos aos nomes de suas classes, que são tradicionalmente mais curtos do que os nomes de namespace e, portanto, mais sujeitos a colisões. A partir de 2007, todas as classes e funções do macOS no ambiente de programação Cocoa são prefixadas com "NS" (por exemplo, NSObject, NSButton) para identificá-las como pertencentes ao macOS ou núcleo do iOS; o "NS" deriva dos nomes das classes conforme definido durante o desenvolvimento do NeXTSTEP .

Como Objective-C é um superconjunto estrito de C, ele não trata os tipos primitivos C como objetos de primeira classe .

Ao contrário do C ++ , Objective-C não oferece suporte à sobrecarga de operador . Também ao contrário do C ++, Objective-C permite que um objeto herde diretamente apenas de uma classe (proibindo a herança múltipla ). No entanto, na maioria dos casos, categorias e protocolos podem ser usados ​​como formas alternativas para alcançar os mesmos resultados.

Como Objective-C usa tipagem dinâmica de tempo de execução e todas as chamadas de método são chamadas de função (ou, em alguns casos, syscalls), muitas otimizações de desempenho comuns não podem ser aplicadas a métodos Objective-C (por exemplo: inlining, propagação constante, otimizações interprocedurais, e substituição escalar de agregados). Isso limita o desempenho de abstrações Objective-C em relação a abstrações semelhantes em linguagens como C ++, onde tais otimizações são possíveis.

Gerenciamento de memória

As primeiras versões do Objective-C não suportavam a coleta de lixo . Na época, essa decisão era uma questão de debate, e muitas pessoas consideravam longos "tempos mortos" (quando Smalltalk realizava a coleta) para tornar o sistema inteiro inutilizável. Algumas implementações de terceiros adicionaram esse recurso (mais notavelmente GNUstep usando Boehm ), e a Apple o implementou a partir do Mac OS X v10.5 . No entanto, em versões mais recentes do macOS e iOS, a coleta de lixo foi preterida em favor da contagem automática de referência (ARC), introduzida em 2011.

Com o ARC, o compilador insere retém e libera chamadas automaticamente no código Objective-C com base na análise de código estático . A automação alivia o programador de ter que escrever no código de gerenciamento de memória. ARC também adiciona referências fracas à linguagem Objective-C.

Diferenças filosóficas entre Objective-C e C ++

O design e a implementação de C ++ e Objective-C representam abordagens fundamentalmente diferentes para estender C.

Além do estilo de programação procedural do C, o C ++ oferece suporte direto a certas formas de programação orientada a objetos , programação genérica e metaprogramação . C ++ também vem com uma grande biblioteca padrão que inclui várias classes de contêiner . Da mesma forma, Objective-C adiciona programação orientada a objetos , tipagem dinâmica e reflexão para C. Objective-C não fornece uma biblioteca padrão per se , mas na maioria dos lugares onde Objective-C é usado, ele é usado com um OpenStep- like biblioteca como OPENSTEP , Cocoa ou GNUstep , que fornece funcionalidade semelhante à biblioteca padrão do C ++.

Uma diferença notável é que Objective-C fornece suporte de tempo de execução para recursos reflexivos , enquanto C ++ adiciona apenas uma pequena quantidade de suporte de tempo de execução para C. Em Objective-C, um objeto pode ser consultado sobre suas próprias propriedades, por exemplo, se ele responderá a uma certa mensagem. Em C ++, isso não é possível sem o uso de bibliotecas externas.

O uso de reflexão faz parte da distinção mais ampla entre recursos dinâmicos (tempo de execução) e recursos estáticos (tempo de compilação) de uma linguagem. Embora Objective-C e C ++ cada um empregue uma combinação de ambos os recursos, Objective-C é decididamente voltado para decisões de tempo de execução, enquanto C ++ é voltado para decisões de tempo de compilação. A tensão entre a programação dinâmica e estática envolve muitas das compensações clássicas em programação: recursos dinâmicos adicionam flexibilidade, recursos estáticos adicionam velocidade e verificação de tipo.

Programação genérica e metaprogramação podem ser implementadas em ambas as linguagens usando polimorfismo de tempo de execução . Em C ++, isso assume a forma de funções virtuais e identificação de tipo em tempo de execução , enquanto Objective-C oferece tipagem dinâmica e reflexão. Ambos Objective-C e C ++ suportam polimorfismo de tempo de compilação ( funções genéricas ), com Objective-C adicionando este recurso apenas em 2015.

Veja também

Referências

Leitura adicional

  • Cox, Brad J. (1991). Programação Orientada a Objetos: Uma Abordagem Evolucionária . Addison Wesley. ISBN 0-201-54834-8.

links externos