em 26/02/2013 09:21
Com os conceitos básicos da linguagem C em sua memória, é hora de começar a se familirizar com o Objective-C. A diferença chave entre essas linguagens é que Objective-C é uma linguagem orientada a objeto, enquanto C é uma linguagem procedural. Isto significa que primeiro você precisa entender os objetos e como se relacionam as classes. Outros conceitos que serão explorados nesse artigo são: objetos de mensagens, encapsulamento e herança.
As origens do Objective-C
Objective-C e Cocoa são 2 componentes chaves da plataforma iOS. Apesar do fato que a plataforma iOS é relativamente jovem, Objective-C foi criada no início dos anos 1980, em StepStone, por Brad Cox e Tom Love. A linguagem foi criada para combinar a robustez e agilidade da linguagem C com a elegância da linguagem Smaltalk. O Objective-C é um superconjunto rigoroso do C e, em contraste, é uma linguagem de programação de alto nível. A diferença entre o Objective-C e o C é que o Objective-C é uma linguagem orientada a objetos enquanto o C é uma linguagem procedural.
Como o iOS pode usar uma linguagem de programação desenvolvida nos anos 1980? Pouco depois da fundação da NeXT, por Steve Jobs, o Objective-C foi licenciado para StepStone. A NeXT criou a NeXTSTEP, um kit de ferramentas de interface de usuário para o sistema operacional neXT, desenvolvido em Objective-C.
Apesar do NeXTSTEP prover um kit de ferramentas revolucionário, o sistema operacional NeXT ganhou pouco espaço no mercado. Em 1996, a Apple comprou a NeXT e o NeXTSTEP foi renomeado para Cocoa. Esse último foi dominante com a introdução do OS X em Março de 2001 e depois com o lançamento do iPhone e do iOS.
Na programação procedural, um programa consiste de uma séria de procedimentos e rotinas que são executadas para alcançar um estado particular. Na programação orientada a objetos, porém, uma coleção de objetos interage e trabalham juntos para completar uma tarefa. Apesar do resultado final poder ser idêntico, a metodologia e os paradigmas fundamentais são substancialmente diferentes. Modularidade e código reutilizável são duas das principais vantagens da programação orientada a objetos, como veremos em breve.
Objective-C, Cocoa and Cocoa Touch
Novos desenvolvedores nos ecossistemas iOS e OS X muitas vezes se confundem com a relação entre Objective-C, Cocoa (OS X), and Cocoa-Touch (iOS). O que é Cocoa-Touch e como ele se relaciona com o Objective-C? Cocoa-Touch é uma API (application programming interface) nativa da Apple para as plataformas iOS e OSX. Objective-C é a linguagem que dá os poderes à Cocoa-Touch. Enquanto esse artigo se concentra principalmente na programação em Objective-C, em artigos futuros nós veremos um pouco mais sobre Cocoa e Cocoa-Touch.
Classes, Objetos e Instâncias
Outro obstáculo para os desenvolvedores novos na programação orientada a objetos é distinguir entre classes, objetos e instâncias. Uma classe é um molde ou diagrama para a criação de objetos, enquanto instâncias são ocorrência únicas de uma classe. Um objeto é uma estrutura de dados que tem um estado e comportamento. Apesar da sútil diferença entre objetos e instâncias, ambos os termos são utilizados indistintamente às vezes.
Vamos ver um exemplo: torradeiras. Antes de uma torradeira ser produzida, engenheiros criam um projeto, que é o equivalente a uma classe. Cada torradeira criada a partir do projeto é uma instância ou uma única ocorrência do projeto. Apesar de cada torradeira ser criada a partir do mesmo projeto (classe), cada uma tem o seu estado (cor, número de aberturas para os pães, etc) e comportamento
Variáveis de Instância e Encapsulamento
O estado de uma instância é armazenado e definido por suas variáveis de instância, ou os atributos de objeto se você quiser. isso nos leva a um outro padrão chave da programação orientada a objetos: encapsulamento. Encapsulamento significa que a representação interna de objeto é privado e apenas do conhecimento do objeto em si. Isso pode parecer uma restrição severa à primeira vista. Porém, o resultado é um código modular e flexível.
Vamos ilustrar o encapsulamento com outro exemplo. A velocidade de um carro é medida por componentes internos, mas que estiver dirigindo o carro sabe a velocidade só de olhar para o marcador no painel. Quem está dirigindo não precisa saber ou entender como os componentes internos do carro medem a velocidade. Da mesma forma, não é preciso que o motorista saiba como o motor funciona para poder dirigir. Os detalhes de como o carro trabalha são ocultos para o piloto. O estado e comportamento do carro são ocultos para o piloto e acessíveis através do painel e componentes como (pedais, volante, botões de comando…)
Outro paradigma forte da programação orientada a objetos é a herança de classe. Quando uma classe A é uma subclasse da classe B, a classe A herda os atributos e comportamentos da classe B. A classe B é definida como classe pai ou superclasse de A. Herança também promove a reusabilidade de código e modularidade.
Métodos são subrotinas associadas com uma classe, e que definem o comportamento da classe e das instâncias. Os métodos de uma classe têm acesso às instâncias internas e podem assim, modificar o estado da instância. Em outras palavras, o estado de uma instância é controlado pelos métodos de uma instância.
Devido ao padrão de encapsulamento, as variáveis de instância de uma classe não podem ser acessadas livremente. Elas são acessadas através de getters and setters, métodos que permitem recuperar e passar valores a variáveis de instância. Properiedades são uma característica do Objective-C, que fazem a criação dos métodos de acesso (getters and setters). Apesar da utilidade desses métodos, rapidamente se torna complicado escrever métodos de acesso para cada variável de instância. Mais adiante veremos outros detalhes sobre propriedades.
Vamos colocar em prática o que foi explicado acima. Para isso, abra o Xcode e crie um novo projeto (File > New > Project), como mostra a figura 1. Como vimos no artigo anterior, selecione o Command Line Tool como template de projeto, que fica na categoria Application, logo abaixo de OS X, como mostra a figura 2.
Preencha os campos conforme a tela a seguir. Para esse projeto, é importante definir o tipo como Foundation. Certifique-se também de que a opção Use Automatic Reference Counting esteja marcada, e clique em Next.
Agora escolha o local onde vai salvar o seu projeto e clicar em Create.
Você poderá observar em seguida que o projeto foi criado de forma diferente do anterior. Veremos essas diferenças posteriormente.
O projeto contém mais arquivos e pastas do que o projeto criado anteriormente. Além do main.m e Books.1, existem 2 novas pastas, Supporting Files and Frameworks, cada uma contendo um item.
Supporting Files contém o arquivo Books-Prefix.pch. A extensão .pch informa que este é um arquivo de cabeçalho pré-compilado. Mais adiante veremos seu propósito.
A pasta Frameworks contém os frameworks que o projeto utilizará e que forem linkados. O que um framework? É um pacote ou diretório que contém uma biblioteca incluindos seus recursos. O conceito de arquivo de cabeçalho ficará claro em um minuto. A pasta bFramework contém um item, Foundation.framework. Quando o projeto foi criado você definiu o seu tipo como Foundation, isso significa que ele está ligado ao framework Foundation. Esse framework é um conjunto fundamental de classes do Objective-C. Veremos mais detalhes desse framework em outro artigo.
Chegou a hora de criarmos a primeira classe. Sempre que você criar um novo arquivo (File > New > File…), será exibida uma lista de templates. Escolha Cocoa na seção OS X e selecione Objective-C class como template para criar a nova classe. Clique em Next para continuar.
Dê o nome de Books para a nova classe e na subclasse escolha NSObject. Como vimos anteriormente, criando a nova classe como uma subclasse de NSObject, ela vai passar a herdar os atributos e comportamentos de NSObject. Fazendo isso, a classe Book jáinicia com as funcionalidades herdadas de NSObject.
Ao clicar em Create, o Xcode adiciona 2 arquivos ao projeto, Book.h and Book.m. Book.h é o arquivo de cabeçalho da classe Book e expõe a interface da classe. A interface da classe contém as propriedade e métodos dela, e também especifica a superclasse. Book.m é o arquivo de implementação da classe e define seu comportamento para a implementação de métodos declarados no arquivo de cabeçalho da classe.
Abra o arquivo Book.h e dê uma analisada no seu conteúdo. Entre as linhas 1 e 7 temos os comentários iniciais do arquivo, e a partir da linha 9 já temos a codificação do mesmo. A primeira linha de código, linha 9, é o import da framework Foundation. Com esse import liberamos o acesso às classes e protocolos declarados na framework Foundation.
A segunda e a terceira linha, respectivamente linhas 11 e 13 no arquivo, forma um par. Em objective-C uma classe sempre começa com a declaração @interface e termina com @end, ambos são diretivas de compilação, que é um comendo ou instrução para o compilador. A diretiva @interface é seguida pelo nome da classe, dois pontos (:) e a superclasse (se for utilizada).
NSObject é a classe root da maioria das classes do Onjective-C. As primeiras duas letras, NS, fazem referência à sua origem, NeXTSTEP, como comentado anteriormente. As classes que herdam de NSObject podem fazer uso de métodos das rotinas básicas de execução.
Antes de fazermos modificações na classe Book, vamos ver um pouco sobre o arquivo de implementação Book.m. Ao invés de importar a framework Foundation, o arquivo de implementação importa o arquivo de cabeçalho da classe Book (Book.h). Por que isso é necessário? O arquivo de implementação precisa saber que propriedades e métodos estão declarados no arquivo de cabeçalho antes de poder implementar o comportamento da classe. A declaração de importação é seguida pela implementação da classe, indicada por @implementation e terminada por @end.
Adicionando propriedades e métodos
A classe Book não é muito útil na implementação atual. Volte para o arquivo de cabeçalho e adicione 3 propriedades: year, title and author, e adicione o método chamado bookInfo. Propriedades são declaradas com a palavra-chave @property e podem ser declaradas em qualquer lugar do bloco @interface da classe. A palavra-chave @property é seguida pelo tipo e o nome da propriedade. Não se esqueça do asterisco (*) na frente das propriedades title e author, porque um objecto Cocoa é sempre referenciado como um ponteiro.
A declaração do método pouco se assemelha a uma função, mas existem algumas diferenças essenciais. A declaração do método se inicia com o sinal de menos (-), indicando que esta é uma instância do método. Métodos de classe são prefixados pelo sinal de mais (+). O sinal de menos (-) é seguido pelo tipo de dado que será retornado, coloca entre entre parênteses, como uma instância de NSString, e o nome do método. O fechamento da declaração do método é feito com um ponto-e-vírgula(;);
Tenho certeza que você está se perguntando o que NSString é e porque precisa ser referenciada como um ponteiro. A classe NSString é um membro da framework Foundation. Declara a interface para um objeto que gerencia uma string imutável. Em artigo anterior, nós vimos que uma string em C pode ser representada por um array de caracteres e isso é o que NSString faz exatamente, ela gerencia um array de caracteres. A vantagem de usar NSString é que o trabalho com cadeias de caracteres fica mais fácil.
Implementando bookInfo
Agora que já declaramos o método bookInfo no arquivo de cabeçalho, é hora de implementar no arquivo de implementação. Abra o arquivo Book.m e adicione o seguinte código após o bloco @implementation.
NSString *bookInfo = [NSString stringWithFormat:@"%@ foi escrito por %@ e publicado em %i", self.title, self.author, self.year];
Envio de mensagens a objetos
Já sabemos que o comportamento de uma classe é definido através de seus métodos. Para chamar um método em um objeto, uma mensagem é enviada para o objeto. Veja o código a seguir para entender esse conceito.
NSString *string = @"Isso é uma frase.";
NSLog(@"O comprimento da frase é %i.n" length);
Na primeira linha nós declaramos uma nova string e definimos o seu valor como constante, colocando-o entre aspas duplas ("), precedido de @.
Na segunda linha nós chamamos o método length, que retorna a quantidade de caracteres que a frase possui. Essa chamada de método é considerada uma mensagem de objeto, pois estamos enviando uma mensagem ao objeto para obtermos um retorno. A variável que receberá esse retorno foi definida com o tipo int, pois o retorno será um valor inteiro.
Na última linha, nós usamos o método NSLog para escrevermos na tela do console o comprimento da frase.
O envio de mensagens a objetos é algo que será feito por repetidas vezes durante o desenvolvimento de aplicativos para iOS, por isso é importante entender a sintaxe. Apesar da sintaxe ser de uma forma diferenciada, se você está começando a estudar o Objective-C, isso não vai ser tão difícil de entender. Entre colchetes está o objecto do lado esquerdo e a mensagem do lado direito.
Métodos que aceitam argumentos podem parecer um pouco diferentes, mas a sintaxe, de um modo geral, é idêntica. A classe NSString, por exemplo, tem outro método chamado substringFromIndex:. Os dois pontos (:) no final do nome indicam que este método aceita argumentos. Veja a seguir como é feita a chamada desse método.
Objective-C é conhecido por ter métodos com nomes muito longos. Veja no próximo exemplo um método que aceita múltiplos argumentos. temos que admitir que por mais que o nome do método seja longo, o seu comportamento fica bem claro, pois o nome diz tudo. O nome do método é dividido em partes, cada parte aceitando um argumento.
NSString *anotherString = [string stringByPaddingToLength:5 [email protected]"outra string" startingAtIndex:2];
Antes de continuarmos, nós precisamos voltar à implementação de bookInfo. A implementação do método se inicia repetindo a declaração do método. O ponto-e-vírgula (;) no final é substituído por um par de chaves, que envolvem a implementação do método. Declaramos primeiro a nova string, bookInfo, e apontamos para ele o seu valor, sendo esse valor criado com os atributos da instância (title, author e year). No final do método, nós retornamos o valor da nova string, porque o tipo de retorno esperado para esse método é é uma string.
Três coisas exigem esclarecimentos:
o método stringWithFormat: é um método de classe e não uma instância do método. Sabemos disso porque o método é chamado na sua própria classe, NSString, não em uma instância dela. Métodos de classe são comuns na programação orientada a objetos.
o formato especificado para um objeto é representado pelo símbolo @ (precedido pelo sinal %). Tanto title quanto author são objetos (strings para ser preciso).
a palavra-chave self sempre referencia a instância de classe. Nesse caso, self se refere à instância Book, na qual o método bookInfo pertence.
Se você trabalhou com outras linguagens de programação orientadas a objetos, o acesso a variáveis de instância em Objective-C pode ser um pouco confuso. Nós não acessamos diretamente uma variável de instância quando escervemos self.title. Isso é nada mais que um atalho para [self title]. Esse último significa que nós usamos o método getter para recuperar o valor da variável title. O mesmo é válido para definirmos um valor para a variável de instância. Veja no código a seguir:
self.title = @"O Hobbit";
//equivalente
[self setTitle:@"O Hobbit"]
id, nill e NULL
Antes de começarmos a usar a classe Book, eu quero falar sobre algumas palavras-chave que confundem as pessoas de vez em quando. Sempre que você quer armazenar um objeto sem definir explicitamente o tipo do objeto, você usa o tipo id, que é também um tipo default para retornos e declarações de argumentos para métodos em Objective-C. O poder e a utilidade do tipo id vai muito mais longe. O tipo id é um componente chave de tipagem e ligação dinâmica do Objective-C.
É importante entender que o tipo id não detém qualquer informação sobre o objeto em si. Em Objective-C, cada objeto sabe a qual classe pertence, e isso é fundamental. Por que isso? Um dos pontos fortes do Objective-C é a tipagem dinâmica, que significa que a verificação de tipo é realizada em tempo de execução, ao invés de ser em tempo de compilação. No entanto, uma vez que o tipo de dado id não diz ao compilador a que classe o objeto pertence, o objeto em si precisa fornecer essa informação para o compilador.
Isso nos leva a outro componente vital de tempo de execução no Objective-C, a ligação dinâmica. Em Objective-C, uma diferença importante entre funções e mensagens é que a mensagem e o objeto de recepção não estão ligados até a execução. O que isso significa e por que isso é importante? Significa que o método invocado em resposta a uma mensagem enviada a um objeto é determinado no tempo de execução quando a mensagem e o objeto são conhecidos. isso é conhecido como ligação dinâmica.
Em Objective-C, a palavra-chave nil é definida como um objeto nulo, que é, um id com valor 0 (zero). "Por baixo dos panos" isso não diferencia nil, Nil e NULL, e isso é possível para enviar mensagens a cada um deles sem lançar uma exceção. A convenção é para usar nil para objetos, Nil para classes, e NULL para outros casos. Ser capaz de enviar mensagens para nil, Nil e NULL tem benefícios, mas isso também tem malefícios.
Abra o arquivo main.m e adicione a a linha de comando para importar o arquivo de cabeçalho da classe Book.
Imediatamente depois de chamar NSLog, adicione o bloco de código a seguir para criar uma instância da classe Book.
book1.title = @"O Hobbit";
Na primeira linha declaramos uma variável do tipo Book e a inicializamos. O primeiro método chamado é o alloc, que aloca memória para o novo objeto e o cria. Em seguida é chamado o método init, que inicializa o novo objeto, para que possa ser utilizado. O método init retorna a instância, e a variável book1 passa poder utilizar os métodos e propriedades da classe Book. As três linhas seguintes são a passagem de valores para as propriedades do objeto, que já vimos anteriormente como funcionam.
Vamos criar agora uma nova instância da classe Book, chamada book2.
book2.title = @"A Sociedade do Anel";
book2.author = @"JRR Tolkien";
book2.year = 1954;
NSArray *books = [[NSArray alloc] initWithObjects: book1, book2, nil];
Na última linha do código acima nós criamos uma instância de NSArray, outra classe da framework Foundation. A classe NSArray é um array que pode armazenar uma lista ordenada de objetos. Repare que assim como foi feito para a classe Books, também foi alocada memória para a classe NSArray. A diferença é que ao invés de apenas inicializar com init, nesse caso usamos initWithObjects: que já inicializa a instância com os valores. É preciso lembrar que nesses casos, o último valor informado para o argumento deve ser nil.
Misturando C e Objective-C
Eu tenho mencionado constantemente que Objective-C é um superconjunto estrito de C que nós podemos combinar livremente C e Objective-C. Vamos ver como isso funciona. Começaremos usando um if/else para verificar a quantidade de objetos do array. Para enviar a mensagem que nos retornará a quantidade de elementos, usamos count.
Se o array contiver objetos, nós executamos um loop para fazer a iteração entre os objetos no array. Durante cada iteração, nós dizemos ao array que queremos o objeto na posição i, e enviamos a mensagem. Vamos obter como retorno uma string, que vamos imprimir no console através de NSLog.
if ([books count] > 0)
for(int i = 0; i < [books count]; i++)
Tenho quase certeza que você está sobrecarregado com informações sobre Objective-C. Isso é normal. Ainda veremos mais algumas informações informações sobre Objective-C nos próximos artigos, antes de entrarmos de fato no desenvolvimento para iOS, usando a iOS SDK.
Para conseguir um desempenho satisfatório nos estudos da iOS SDK é necessário dominar um pouco o Objective-C, para não tornar o estudo mais complicado. O caminho pode parecer longo, mas será prazeroso e incansável.