10 de junho de 2011

Objective-C - Noção básica iPhone

Objective-C

Introdução

    Este breve tutorial foi criado para suprir a falta de informação sobre a linguagem Objective-C no Brasil, e com isso incentivar sua popularização. Para aqueles que já programam em C, a adaptação ao paradigma de orientação à objetos por meio da linguagem Objective-C ocorre de forma simples e natural, visto que sintaticamente apenas poucas construções são adicionadas. Observamos que no momento este não é um tutorial completo, ou seja, conhecer um pouco de C será não apenas de grande valia, como praticamente necessário para construir programas mais complexos. Devemos salientar também que por enquanto não iremos utilizar o framework “Foundation”, nem qualquer outro, nos mantendo nos rudimentos da linguagem.A linguagem Objective-C foi criada por Brad Cox e sua empresa, a StepStone Corporation, no início da década de 80. Em 88 ela foi licenciada pela NeXT, tornando-se a linguagem de desenvolvimento do NeXTstep. O suporte do GNU/gcc foi acrescentado em 1992. Em 1994 as empresas NeXT Computer e Sun Microsystems criaram uma especificação do NeXTstep conhecida como OpenStep. A implementação da Free Software Foundation da especificação OpenStep é denominada GNUStep.
    Atualmente Objective-C é utilizada como a principal linguagem de programação do MacOS X, que é baseado no NeXTstep. A versão da Apple do ambiente NeXTStep/GNUStep com adições é denominada Cocoa.

Características

    Objective-C é apenas um conjunto de adições à linguagem C. Ela dá ao C suporte à construções orientadas a objetos como as da Smalltalk.

    Objective-C suporta polimorfismo, posing, categorias, e é uma linguagem dinâmica, com typing e binding dinâmicos. Com Objective-C você pode adicionar classes e categorias em tempo de execução de forma fácil. E tudo isso com uma sintaxe de mensagem simples e elegante: olhe os títulos destas subseções e você já saberá como conversar com os objetos!

    Objective-C realiza chamadas de mensagem dinâmicas rapidamente, entre 1,5 e 2,0 vezes o tempo de uma chamada de função em C.

Programação Orientada à Objetos

    O conceito de orientação à objetos é na verdade algo muito simples. Diariamente utilizamos objetos para realizar tarefas, e programar OO não é nada alem disso.Programadores mais antigos estão acostumados a pensar a programação separando dados e fuções que seriam aplicadas neles. A própria forma de pensar era diferente; para alguns problemas era óbvia a necessidade de uma integração maior entre os dados e as funções, e isto foi o aparecimento do objeto.
    Tomando um exemplo simples: supomos que você possua uma televisão. A não ser que você seja um milhonário ou um professor pardal, essa televisão deve ter saído de uma linha de montagem, sendo assim igual a muitos outros aparelhos do mesmo modelo. Entretanto essa é a sua televisão, e ela possui um número de identificação (ou número serial) único.
    No exemplo acima e agora traduzindo para o conceito de OO, sua televisão é uma instância da classe televisão. E cada vez que uma nova televisão é produzido, uma nova instância da classe televisão é criada.

    Existem também certas coisas que você pode fazer com a sua televisão, como ligar e desligar, modificar o volume e configurações de cor, ligar o PIP, etc. As funções realizadas com a televisão, em OO, denominam-se métodos. Em alguns casos os métodos são aplicados na instância da classe, em outros eles são aplicados na classe em si. Por exemplo, ligar a televisão seria um método de instância e descobrir quantos televisores foram produzidos por um determinado fabricante seria um método de classe.

    Quando vamos comprar um televisor em alguma loja, costumamos encontrá-los agrupados junto com tocadores de DVD, por exemplo. Bem, isso não se dá por acaso: televisores, assim como tocadores de DVD são eletrônicos, e com isso possuem características e funções em comum. Podemos então pensar nestes dois aparelhos como sendo instâncias de uma classe denominada eletrônicos. Entretanto não queremos instâncias da classe eletrônicos, e sim das classes televisão e toca-DVD, logo dizemos que as classes televisão e toca-DVD são subclasses (ou classes filhas) da superclasse (ou classe pai) eletrônicos. Esse é um conceito muito importante em orientação à objetos, e é denominado herança: quando criamos uma subclasse ela herda as características e métodos de sua superclasse. Nesse caso podemos dizer que nunca iremos criar uma instância da classe eletrônicos, logo esta é denominada uma classe abstrata, ou seja, uma classe que serve apenas para ser superclasse de outras classes.

Ambiente de programação

    Para programar em Objective-C, tal como em qualquer outra linguagem moderna, você precisa de um editor e um compilador. Ambos estão disponíveis gratuitamente em muitos dos sistemas operacionais atuais.No MacOS X, você deve instalar os “Developer Tools” gratuitos da Apple, caso eles não estejam presentes no seu sistema. Essas ferramentas incluem dentre muitas outras coisas o compilador gcc e um ambiente de programação extremamente eficiente, denominado Xcode.
    No Linux você já deve ter o gcc instalado, bem como muitos editores de texto (vi, pico, emacs). Você tambem pode utilizar algum ambiente de programação, como o KDevelop ou ferramentas GNUStep.

    No Windows você pode instalar o compilador Objective-C que acompanha o sistema MinGW (http://www.mingw.org). Para escrever os códigos, qualquer editor de texto puro, como o Notepad, ou algum ambiente de programação devem servir.

    Nos três sistemas, compilar um programa utilizando a linha de comando resume-se ao gcc (para projetos maiores costuma-se utilizar ferramentas como make, automake, etc), cujo formato geral de chamada é:

    gcc arquivos.m -o nome_do_programa -l objc

    Os arquivos de código da linguagem Objective-C possuem sufixo “.m”, enquanto que os cabeçários possuem, como em C, sufixo “.h”.

    Por algum motivo alguem na FSF não parece gostar muito de uma diretriz de pré-processamento (mais sobre isso depois) do Objetive-C denominada “#import”, que serve para substituir “#include” de forma a não ser necessário escrever “#ifndef #define #endif” para não incluir o mesmo arquivo várias vezes, e com isso as versões do gcc utilizadas em computadores com Linux e Windows dão “Warnings” na hora de compilar. Para acabar com esse problema, utilize a chamada do gcc com “-Wno-import”. Computadores com o gcc fornecido pela Apple não sofrem desse problema.

    Para compilar programas utilizando o Xcode da Apple você deve criar um novo projeto do tipo “Cocoa Application” e sobrescrever o arquivo “main.m” que será criado automaticamente.

Meu primeiro programa: classes, objetos e métodos

    Nesta seção vamos implementar um programa básico, já utilizando conceitos de programação orientada à objetos.Os programas em Objective-C possuem quatro partes bem definidas . A primeira define diretrizes para o pré-processador, a segunda define as classes e métodos, a terceira a implementa os métodos e a quarta é o programa em si. Mais tarde mostraremos como cada parte pode ficar armazenada em um arquivo separado, facilitando a organização do programa em si.
    Para facilitar, podemos iniciar lendo o programa a seguir que define nossa primeira classe, o Gato. Esse dócil animal mia, logo vamos implementar uma classe “Gato” que possui um método chamado “miau” que serve apenas para escrever “Miaau!” na tela. Veja o código:

    meuGato.m

    // // Programa Gato //  // por Alberto GOK Martins // para o tutorial de ObjC //

    // aqui comecam as diretrizes do pre-processador
    #import <stdio.h>
    #import <objc/Object.h>

    // aqui comeca a definicao das interfaces
    @interface Gato: Object

    -(void) miau;

    @end

    // aqui comecam as implementacoes dos metodos
    @implementation Gato

    -(void) miau
    {
    printf("Miaau!\n");
    }

    @end

    // aqui comeca o programa
    int main( int argc, const char *argv[] )
    {
    Gato *meuGato;

    meuGato = [Gato alloc];
    meuGato = [meuGato init];

    [meuGato miau];

    [meuGato free];

    return 0;
    }


    Todas as linhas que iniciam por “
    //” são linhas de comentários. Estas são ignoradas, logo é permitido escrever qualquer coisa após as duas barras. Comentários são utilizados para documentar um programa, facilitando sua compreensão.

    Outra forma de inserir comentários em programas Objective-C é utilizar os caracteres “/*” no início do comentário e “*/” no final; desta forma pode-se criar blocos de comentário com muitas linhas, sem que seja necessário escrever “//” a cada linha nova.

    As linhas iniciadas por “#” são diretrizes de pré-processamento. No programa acima utilizamos “#import <…>”, que diz para o compilador localizar, processar e importar informações dos arquivos “stdio.h” e “objc/Object.h”, ambos arquivos do sistema. Estes arquivos contém informações sobre a rotina “printf” e a classe “Object”, que foram utilizadas no nosso programa.

    A segunda parte do arquivo consiste na definição das Classes e de suas interfaces. Quando nós definimos uma nova classe é necessário dizer ao compilador algumas coisas. A primeira delas é falar de onde a classe vem, ou seja quem é a sua superclasse. Utilizamos para isso a seção “@interface”, cujo formato geral é:


    @interface NovaClasse: SuperClasse
    {
    Declaração_de_variáveis;
    }

    Declaração_de_métodos;

    @end

    No nosso programa definimos a classe “Gato”, que por enquanto não possui variáveis e apenas um método, o “miau”. Esse é um método da instância da classe “Gato” pois sua declaração é iniciada com o símbolo “-”. Poderíamos também criar um método da própria classe “Gato” utilizando o símbolo “+” para, por exemplo, contar o número de gatos que foram criados no programa.

    Após o sinal “-” temos a declaração entre parênteses do que o método irá retornar; no caso, “void”, indica que nada será retornado. Finalmente temos o nome do método. Mais adiante forneceremos o formato geral de declaração de métodos.

    A terceira parte do arquivo é a implementação dos métodos da classe. Ela define os métodos, ou seja contém seus códigos, declarados na parte “@interface”. O formato é:

    @interface NovaClasse

    Definição_de_métodos;

    @end

    A definição de um método nada mais é do que sua declaração sem o caractere “;” acompanhada por seu código, que é colocado entre chaves “{” e “}”. No nosso programa, o método “miau” tem o código definido por uma instrução da biblioteca “stdio.h” chamada “printf”, que serve para mostrar caracteres na tela.

    A quarta e última parte do arquivo, é o programa em si. Esta parte contem a rotina denominada “main” que indica precisamente onde o programa deve iniciar sua execução. Ela inicia com a palavra “int”, especificando que “main” deve retornar um valor inteiro ao sistema quando terminar sua execução. As palavras que aparecem entre parêntesis “()” servem para lidar com argumentos passados pela linha de comando.

    A primeira linha da rotina “main” define uma variável chamada “meuGato”, dizendo que “meuGato” é um objeto que guardará valores da classe “Gato”. O asterísco diz que “meuGato” é um ponteiro para um “Gato”.

    Na segunda linha alocamos espaço na memória para o “meuGato”, e na terceira inicializamos as variáveis que podem estar presentes dentro do “meuGato”. Observe que na segunda linha passamos uma mensagem para a classe “Gato”, enquanto que na terceira a mensagem vai para a instância “meuGato”. Os métodos “alloc” e “init” não foram escritos por você, entretanto podemos utilizá-los pois foram definidos para a classe “Object”, que é a superclasse de “Gato”.

    Na quarta linha passamos uma mensagem para o objeto “meuGato”, dizendo para ele miar, ou seja, executar o método “miau” que implementamos.

    Na quinta linha passamos uma mensagem para “meuGato” dizendo que ele não será mais necessário, e que podemos liberar a memória que foi reservada à ele.

    Na sexta linha especificamos o valor que o programa deve retornar ao sistema; no caso 0, indicando que tudo ocorreu bem.

    Vamos finalmente compilar e executar o nosso programa. Para gerar o executável deve-se utilizar um compilador, no caso o gcc, e como visto anteriormente sua chamada (desta vez já com os nomes dos arquivos corretos) é:

    gcc meuGato.m -o meuGato -l objc

    Executando o arquivo “meuGato” deve-se obter a seguinte saída:

    Miaau!

    Um gato mais real…

    Vejamos agora um programa um gato um pouco mais elaborado. Todos devemos concordar que os gatos possuem certas características, como cor do pêlo, tamanho, peso, etc. Logo está faltando algo ao nosso gato. No programa a seguir implementamos o peso, bem como funções para defini-lo e obtê-lo; fica para você a tarefa de implementar as outras características.

    //
    // Programa Gato com caracteristica
    // // por Alberto GOK Martins // para o tutorial de ObjC //

    // aqui comecam as diretrizes do pre-processador
    #import <stdio.h>
    #import <objc/Object.h>

    // aqui comeca a definicao das interfaces
    @interface Gato: Object
    {
    double peso;
    }

    -(void) miau;
    -(void) setPeso: (double) p;
    -(double) getPeso;

    @end

    // aqui comecam as implementacoes dos metodos
    @implementation Gato

    -(void) miau
    {
    printf("Miaau!\n");
    }

    -(void) setPeso: (double) p
    {
    peso=p;
    }

    -(double) getPeso
    {
    return peso;
    }

    @end

    // aqui comeca o programa
    int main( int argc, const char *argv[] )
    {
    Gato *meuGato;
    Gato *meuGato2;

    meuGato = [[Gato alloc] init];
    meuGato2 = [[Gato alloc] init];

    [meuGato setPeso: 4.2];
    [meuGato2 setPeso: 4.9];
    [meuGato miau];
    [meuGato2 miau];
    printf("meuGato pesa: %lf kg\n",[meuGato getPeso]);
    printf("meuGato2 pesa: %lf kg\n",[meuGato2 getPeso]);

    [meuGato free];
    [meuGato2 free];

    return 0;
    }


    A primeira adição no programa acima em relação ao original são as declarações dos métodos “setPeso” e “getPeso”. O primeiro recebe um valor real do tipo double, e o segundo retorna um valor tipo double. Estamos prontos para enunciar um formato geral de declaração de métodos:

    tipo_de_método(tipo_do_retorno_do_método) nome_do_método: (tipo_do_argumento) argumento;

    exemplo de método de classe:

    +(int) quantosGatos;

    exemplo de método de instância:

    -(void) setPeso: (double) p;


    Na parte de implementação temos as implementações de “setPeso” e “getPeso”.

    Na rotina “main” podemos notar que estamos criando dois gatos diferentes. O primeiro pesa 4.2kg, enquanto que o segundo 4.9kg. Devemos observar que, de modo distinto do programa original, estamos alocando memória e inicializando “meuGato” ao mesmo tempo. Essa é uma construção muito utilizada, junto com a alternativa seguinte:

    Gato *meuGato = [[Gato alloc] init];

    Podemos observar, ainda dentro da rotina “main”, como passar argumentos para o objeto “meuGato”, com o método setPeso. Ou também como obter o valor de uma característica do “meuGato”, com o método “getPeso” e imprimi-lo na tela com a função “printf”.

    A saída agora deve ser:

    Miaau!
    Miaau!
    meuGato pesa: 4.200000 kg
    meuGato2 pesa: 4.900000 kg

Mais sobre classes, objetos, métodos

    Na seção anterior vimos como passar um argumento para um método. Entretanto muitas vezes é desejável poder passar mais do que um argumento por vez, por exemplo para definir a posição (x,y) de um ponto no R2. Felizmente existe um tipo de construção que nos permite fazê-lo:
    -(tipo) nome: (tipo) variável nome: (tipo) variável;

    exemplo, um método para definir as posições de um ponto no R
    2:

    -(void) setX: (double) x andY: (double) y;

    passando a mensagem:

    [Objeto setX: 1.511 andY: 2.332];

    Geralmente é interessante ter métodos para definir cada argumento separadamente, ainda que existam os métodos para definir múltiplos argumentos de uma só vez.

    Um método também pode retornar um objeto. Por exemplo, poderíamos implementar um método de instância na classe “Gato” que definisse uma reprodução sexuada. Para isso temos que ter duas instâncias da classe “Gato” allocadas e inicializadas; uma para receber a mensagem e outra para ser o objeto a ser recebido como argumento da mensagem. Devemos ter também um objeto do tipo “Gato”, que armazenará o gato filho. Acompanhe a definição do método “reproduzir”:

    <novo método>
    -(Gato *) reproduzir: (Gato *) Gato2
    {
    // funcao para criar um gato filho com a media dos pesos dos pais // antes de algum protesto, nao estamos tentando ser biologicamente corretos!

    Gato *Gatinho=[[Gato alloc] init];
    double pesoGatinho;

    pesoGatinho = (peso+[Gato2 getPeso])/2;

    [Gatinho setPeso: pesoGatinho];

    return Gatinho;
    }

    <novo main>
    int main( int argc, const char *argv[] )
    {
    Gato *meuGato = [[Gato alloc] init];
    Gato *meuGato2 = [[Gato alloc] init];
    Gato *meuGatoFilho;

    [meuGato setPeso: 4.2];
    [meuGato2 setPeso: 4.9];
    [meuGato miau];
    [meuGato2 miau];

    meuGatoFilho = [meuGato reproduzir: meuGato2];
    [meuGatoFilho miau];

    printf("meuGato pesa: %lf kg\n",[meuGato getPeso]);
    printf("meuGato2 pesa: %lf kg\n",[meuGato2 getPeso]);
    printf("meuGatoFilho pesa: %lf kg\n",[meuGatoFilho getPeso]);

    [meuGato free];
    [meuGato2 free];
    [meuGatoFilho free];

    return 0;
    }

    Em Objective-C você pode se referir ao próprio objeto que está recebendo a mensagem, usando para isso a palavra “self”. Isso tem muitas utilidades, por exemplo para executar um método que precise executar outros métodos. Um exemplo não muito bom seria criar um método “comer” para o nosso gato que utilizasse o método “setPeso” (como [self setPeso: 5]) para engordá-lo; nesse caso poderíamos alterar o valor de “peso” sem recorrer ao “self”, porém caso tivéssemos algum método que realizasse muitas tarefas isso seria de grande utilidade.

    Agora uma dica de organização: tipicamente os programadores dividem suas classes em arquivos separados, nomeados “classe.h” e “classe.m”; o primeiro contém a seção “@interface” e o segundo “@implementation”. O arquivo de interface diz ao compilador como a sua classe é, enquanto que o arquivo de implementação contém o código verdadeiro da classe.

    Para realizar ligar os diversos arquivos devemos compilar o programa de uma forma especial, bem como nos utilizar da diretriz de pré-processamento “#import”. Observe a nova implementação do programa do gato:

    Gato.h

    //

    // Classe Gato - interface //  // por Alberto GOK Martins // para o tutorial de ObjC //

    #import <objc/Object.h>

    @interface Gato: Object
    {
    double peso;
    }

    -(void) miau;
    -(void) setPeso: (double) p;
    -(double) getPeso;
    -(Gato *) reproduzir: (Gato *) Gato2;

    @end

    Gato.m

    // // Classe Gato - implementacao // // por Alberto GOK Martins // para o tutorial de Objc //

    #import <stdio.h>
    #import "Gato.h"

    @implementation Gato

    -(void) miau
    {
    printf("Miaau!\n");
    }

    -(void) setPeso: (double) p
    {
    peso=p;
    }

    -(double) getPeso
    {
    return peso;
    }

    -(Gato *) reproduzir: (Gato *) Gato2
    {
    // funcao para criar um gato filho com a media dos pesos dos pais // antes de algum protesto, nao estamos tentando ser biologicamente corretos!

    Gato *Gatinho=[[Gato alloc] init];
    double pesoGatinho;

    pesoGatinho = (peso+[Gato2 getPeso])/2;

    [Gatinho setPeso: pesoGatinho];

    return Gatinho;
    }

    @end

    main.m

    // // Programa Gato com caracteristica // em multiplos arquivos // // por Alberto GOK Martins // para o tutorial de ObjC //

    #import <stdio.h>
    #import "Gato.h"

    int main( int argc, const char *argv[] )
    {
    Gato *meuGato = [[Gato alloc] init];
    Gato *meuGato2 = [[Gato alloc] init];
    Gato *meuGatoFilho;

    [meuGato setPeso: 4.2];
    [meuGato2 setPeso: 4.9];
    [meuGato miau];
    [meuGato2 miau];

    meuGatoFilho = [meuGato reproduzir: meuGato2];
    [meuGatoFilho miau];

    printf("meuGato pesa: %lf kg\n",[meuGato getPeso]);
    printf("meuGato2 pesa: %lf kg\n",[meuGato2 getPeso]);
    printf("meuGatoFilho pesa: %lf kg\n",[meuGatoFilho getPeso]);

    [meuGato free];
    [meuGato2 free];
    [meuGatoFilho free];

    return 0;
    }

    Preste especial atenção em como a diretiva “#import” é utilizada. O arquivo “Gato.h” precisa de informações sobre a classe “Object” que foi definida no arquivo “objc/Object.h”, e por isso devemos importá-lo em “Gato.h”. Já o arquivo “Gato.m” deve importar o arquivo de interface de sua classe, o “Gato.h”, bem como “stdio.h”, pois utiliza a função “printf”. Finalmente, o programa principal deve importar o arquivo “stdio.h” pela função “printf” e o arquivo “Gato.h” por instanciar objetos da classe “Gato”. Isso pode parecer um pouco confuso, mas na verdade o conceito é bastante simples: deve-se importar tudo o que for utilizar.

    Para compilar programas com multiplos arquivos no gcc, use a seguinte linha de comando:

    gcc arquivo1.m arquivo2.m main.m -o nome_do_programa -l objc

    no caso do nosso programa, use:

    gcc Gato.m main.m -o gato -l objc

    execute o programa e veja o resultado; ele deve ser similar ao apresentado abaixo:

    Miaau!
    Miaau!
    Miaau!
    meuGato pesa: 4.200000 kg
    meuGato2 pesa: 4.900000 kg
    meuGatoFilho pesa: 4.550000 kg

Herança

    Nesta seção falaremos sobre um dos conceitos mais importantes da programação orientada a objetos: a herança.Uma classe pode ter a si mesma como superclasse, e essa classe, que não possui superclasse é denominada classe raiz (ou root). A classe raiz pode ser programada por você mesmo, ou então como é mais comum, ser a classe Object, descrita em “objc/Object.h” ou a classe NSObject, descrita em “Foundation/NSObject.h”.
    A classe que foi criada neste tutorial é uma subclasse de Object, e com isso ela herda certos métodos, como free e outros que foram definidos para a classe Object. No entanto suponha que agora seja necessário criar um classe para cachorros. Bem, poderíamos criar uma subclasse de Object denominada Cachorro e implementar funções como setPeso e getPeso idênticas às da classe Gato. Uma solução melhor, seria criar uma subclasse de Object denominada Animal, e as classes Cachorro e Gato seriam subclasses da classe Animal, que por sua vez seria uma classe abstrata, visto que nunca iremos criar uma instância de Animal.

    No exemplo abaixo fizemos justamente isso.

    Animal.h

    // // Classe Abstrata Animal - interface // // por Alberto GOK Martins // para o tutorial de Objc //

    #import <objc/Object.h>

    @interface Animal : Object
    {
    double peso;
    }

    -(void) setPeso: (double) p;
    -(double) getPeso;

    @end

    Animal.m

    // // Classe Abstrata Animal - implementacao // // por Alberto GOK Martins // para o tutorial de Objc //

    #import "Animal.h"

    @implementation Animal

    -(void) setPeso: (double) p
    {
    peso=p;
    }

    -(double) getPeso
    {
    return peso;
    }

    @end

    Gato.h

    // // Classe Gato - interface // // por Alberto GOK Martins // para o tutorial de Objc //

    #import "Animal.h"

    // aqui comeca a definicao das interfaces
    @interface Gato: Animal

    -(void) miau;

    @end

    Gato.m

    // // Classe Gato - implementacao // // por Alberto GOK Martins // para o tutorial de Objc //

    #import "Gato.h"
    #import <stdio.h>

    // aqui comecam as implementacoes dos metodos
    @implementation Gato

    -(void) miau
    {
    printf("Miaau!\n");
    }

    @end

    Cachorro.h

    // // Classe Cachorro - interface // // por Alberto GOK Martins // para o tutorial de Objc //

    #import "Animal.h"

    // aqui comeca a definicao das interfaces
    @interface Cachorro: Animal

    -(void) auau;

    @end

    Cachorro.m

    // // Classe Cachorro - implementacao // // por Alberto GOK Martins // para o tutorial de Objc //

    #import "Cachorro.h"
    #import <stdio.h>

    // aqui comecam as implementacoes dos metodos
    @implementation Cachorro

    -(void) auau
    {
    printf("AuAu!\n");
    }

    @end

    main.m

    // // Programa de Animais com heranca // // por Alberto GOK Martins // para o tutorial de ObjC //

    #import <stdio.h>
    #import "Gato.h"
    #import "Cachorro.h"

    // aqui comeca o programa
    int main( int argc, const char *argv[] )
    {
    Gato *meuGato = [[Gato alloc] init];
    Gato *meuGato2 = [[Gato alloc] init];
    Cachorro *meuCachorro = [[Cachorro alloc] init];
    Cachorro *meuCachorro2 = [[Cachorro alloc] init];

    [meuGato setPeso: 4.2];
    [meuGato2 setPeso: 4.9];
    [meuGato miau];
    [meuGato2 miau];

    printf("meuGato pesa: %lf kg\n",[meuGato getPeso]);
    printf("meuGato2 pesa: %lf kg\n",[meuGato2 getPeso]);

    [meuCachorro setPeso: 10.2];
    [meuCachorro2 setPeso: 13.3];
    [meuCachorro auau];
    [meuCachorro2 auau];

    printf("meuCachorro pesa: %lf kg\n",[meuCachorro getPeso]);
    printf("meuCachorro2 pesa: %lf kg\n",[meuCachorro2 getPeso]);

    [meuCachorro free];
    [meuCachorro2 free];
    [meuGato free];
    [meuGato2 free];

    return 0;
    }

    Observe que no exemplo acima o método “reproduzir” nao existe mais. Optamos por não escrevê-lo, pois embora essa seja uma atividade comum para todos os animais a reprodução de animais diferentes deve gerar objetos de classes diferentes, e não um animal genérico da classe animal. Para contornar o problema você poderia escrever dois métodos, um para cada classe, denominados “reproduzir”, que embora partilhassem o mesmo nome seriam um pouco diferentes; afinal você não espera que um casal de cachorros gerem filhos gatos. Implementar um método comum exigirá um pouco mais de conhecimento, e isso será realizado na próxima seção, denominada Polimorfismo, Typing dinâmico e Binding dinâmico.

    Suponha agora que você queira que o método getPeso para um gato retorne o peso em gramas e não em quilos. Bem, você pode modificar a implementação original de getPeso na clase Animal, porém isso modificará também o retorno do getPeso para cachorros. Para solucionar este problema pode-se utilizar um conceito denominado overriding.

    Overriding é simplesmente sobrescrever um método. Quando você cria um método com o mesmo nome de um outro método que foi herdado pela classe, vale o método da classe e não o da superclasse. Logo, você poderia criar um método dentro da classe Gato com o mesmo nome (no caso, getPeso) e com isso apenas o método da classe Gato seria afetado, permitindo que a classe Cachorro continuasse retornando o peso em kilogramas. Experimente, realizando overriding de getPeso da classe Gato (não esqueça de modificar o printf da rotina main para escrever “g” ao invés de “kg”).

    Dentro de classes podemos também instanciar objetos. Por exemplo, suponhamos que todos os animais (logo todas as subclasses da classe Animal – novamente, não estamos tentando ser biologicamente corretos!) tenham olhos. Podemos então, definir uma classe denominada Olho, e instanciá-la dentro da classe Animal. Observe a interface e implementação da classe Olho, bem como os novos arquivos da classe Animal.

    Olho.h

    // // Classe Olho - interface // // por Alberto GOK Martins // para o tutorial de Objc //

    #import <objc/Object.h>

    @interface Olho : Object
    {
    int estado_da_pupila;
    }

    -(void) setEstado_Da_Pupila: (int) pupil;
    -(void) printEstado_Da_Pupila;

    @end

    Olho.m

    // // Classe Olho - implementacao // // por Alberto GOK Martins // para o tutorial de Objc //

    #import "Olho.h"

    @implementation Olho

    -(void) setEstado_Da_Pupila: (int) pupil
    {
    if(pupil==0) estado_da_pupila=0;
    if(pupil==1) estado_da_pupila=1;
    if(pupil==2) estado_da_pupila=2;
    }

    -(void) printEstado_Da_Pupila
    {
    if(estado_da_pupila==0)
    {
    printf("A pupila está contraída\n");
    }
    if(estado_da_pupila==1)
    {
    printf("A pupila está normal\n");
    }
    if(estado_da_pupila==2)
    {
    printf("A pupila está dilatada\n");
    }
    }

    @end

    Animal.h

    // // Classe Abstrata Animal - interface // // por Alberto GOK Martins // para o tutorial de Objc //

    #import <objc/Object.h>
    #import "Olho.h"

    @interface Animal : Object
    {
    double peso;
    Olho *AOlho;
    }

    -(void) setPeso: (double) p;
    -(double) getPeso;
    -(void) printEstado_Da_Pupila;
    -(void) setEstado_Da_Pupila: (int) edp;

    @end

    Animal.m

    // // Classe Abstrata Animal - implementacao // // por Alberto GOK Martins // para o tutorial de Objc //

    #import "Animal.h"

    @implementation Animal

    -(void) setPeso: (double) p
    {
    peso=p;
    }

    -(double) getPeso
    {
    return peso;
    }

    -(void) printEstado_Da_Pupila
    {
    // funcao para imprimir o estado da pupila de um animal

    if(AOlho)
    [AOlho printEstado_Da_Pupila];
    else
    printf("Não foi definido um estado da pupila deste animal.\n");
    }

    -(void) setEstado_Da_Pupila: (int) edp
    {
    [AOlho free];
    AOlho = [[Olho alloc] init];
    [AOlho setEstado_Da_Pupila: edp];
    }

    @end

    O código que deve ser utilizado para testar o funcionamento do Olho em main.m é:

    [meuGato setEstado_Da_Pupila: 1];
    [meuGato printEstado_Da_Pupila];

    Obtivemos agora um enorme problema. Como é que vamos liberar a memória alocada para o Olho do Animal? Como não podemos acessar diretamente o objeto AOlho não podemos liberar a memória sem criar um método pertencente à classe Animal específico para isso; ou então, como saída mais inteligente, realizar um overriding do método free.

    Caso não liberemos a memória teremos um problema que se denomina vazamento de memória, ou memory leakage. Bem, para realizar o overriding de free precisamos saber como liberar a memória alocada pelo próprio objeto, no entanto como o método free é agora diferente do original ele não mais saberá como liberar a memória alocada para o objeto. A saída para isso é a instrução super que se refere a classe pai do recebedor da mensagem. Super chamará não o método da classe atual, mas aquele que foi herdado. Desta forma podemos sempre nos referir ao método antigo de todos os métodos que foram implementados através de overriding.

    Logo a implementação do método free para a classe Animal deve ser:

    -(id) free
    {
    [AOlho free];
    return [super free];
    }

    Deixaremos para nos preocupar com a palavra id na próxima seção


Polimorfismo, Typing dinâmico e Binding dinâmico

    Polimorfismo é um conceito muito simples. Ele nada mais é do que a habilidade que uma linguagem orientada à objetos tem de definir métodos com o mesmo nome para classes diferentes. Cada definição de classe guarda o código necessário para seus métodos.Typing dinâmico é a característica que a linguagem tem de poder postergar a determinação da classe de um objeto até o tempo de execução do programa, enquanto que Biding dinâmico é poder postergar a determinação do método que será invocado em um objeto até o tempo de execução.
    Para realizar o typing e o binding dinâmicos utiliza-se o tipo id. Este é um tipo de objeto genérico, que pode ser utilizado para guardar objetos pertencentes a qualquer classe. Na seção anterior utilizamos o tipo id para realizar o overriding do método free. Vejamos agora, como implementar o método reproduzir na classe Animal, e permitir que a classe Gato e Cachorro se reproduzam, sem que seja necessário criar dois métodos distintos:
    -(id) reproduzir: (id) Animal2
    {
    // funcao para criar um animal filho com a media dos pesos dos pais // antes de algum protesto, nao estamos tentando ser biologicamente corretos! // existe um teste para verificar se os animais sao da mesma classe.

    id Filho=[[[self class] alloc] init];

    if ([self class] == [Animal2 class])
    {
    printf("O filho nasceu sadio!\n");
    [Filho setPeso: (peso+[Animal2 getPeso])/2];
    }
    else
    {
    printf("O filho nasceu morto, pois os pais são de classes diferentes!\n");
    [Filho setPeso: 0];
    }
    return Filho;

    }

    Na implementação da função acima utilizamos o tipo id, pois não sabemos qual será a subclasse de Animal que será passada nem a classe que irá retornar. Utilizamos também a palavra self que serve para se referir ao próprio objeto, logo dizer [self class] é uma forma de pedir para o objeto retornar a própria classe; no método reproduzir precisamos criar um filho da mesma classe dos pais, logo usamos [self class] como a classe do filho.

Últimas palavras

    Esse tutorial deve ser aprimorado, com o passar do tempo e com sua ajuda. Por favor, não deixe de enviar um email dizendo o que achou, se conseguiu aprender alguma coisa ou se não conseguiu aprendeu nada. Por enquanto é isso. Caso você queira se aprofundar na linguagem eu sugiro os links e referências presentes na seção Referências e links.

Referências e links

Página por Alberto G. O. K. Martins – algol@astro.iag.usp.br
Design based on the Dekorte’s Objective-C webpage. CSS additions by Alberto G. O. K. Martins.
Qualquer contribuição, alteração, comentário ou sugestão, favor enviar para o email algol@astro.iag.usp.br.

6 de junho de 2011

Começando no universo do iOS - iPhone

Bom dia galera,


Vou iniciar um novo assunto no blog e de interesse de muita gente atualmente, iPhone.


Acho que todo mundo hoje em dia quer desenvolver app's para iPhone, mas vem a seguinte pergunta:

- Como eu começo?


Seguindo pelo inicio de tudo recomendo você a criar uma conta na apple no canal "iOS Dev Center".


Nesse canal você poderá ter tudo para seu início ao universo iOS.


Você vai precisar do Xcod e iOS SDK para desenvolver e emula um iPhone em seu Mac caso ainda não tenha um iPhone.


Próximo post vou mostrar alguns código iniciais para fazer um "Hellow Word".


Até!