Quaddro + MacMagazine: programando em Objective-C — classes e propriedades

Tipo de projeto no Xcode

Depois de uma série sobre os tópicos básicos de programação em C, chegou a hora de darmos os primeiros passos com Objective-C. E isso nos leva a um outro nível de programação: a orientação a objetos.

Pré-requisitos

The Warpath, de Conner Youngblood

Conceitos

Quando eu escrevi o primeiro artigo técnico desta série abordando a linguagem C, citei entre outras características o fato de C ser procedural. E esse é o gancho para conectar o que já estudamos com o novo assunto (orientação a objetos).

O fato de a linguagem C ser procedural a define como uma linguagem que baseia a sua execução na chamada de procedimentos (funções para aqueles que leram o nosso último artigo). Na época do seu lançamento essa abordagem era mais do que suficiente para uma programação otimizada, porém com o tempo e a busca constante por melhorias novos estudos levaram a comunidade da computação a um novo e mais produtivo paradigma: a orientação a objetos.

Aposto que muitos de vocês já ouviram sobre orientação a objetos, mas imagino que alguns ainda devam ter dúvidas no ar. Ouço muitos alunos iniciantes da Quaddro com a famosa pergunta: “Afinal de contas, o que é orientação a objetos?”

Comparando com a forma de programar em C, a orientação a objetos diz que, em vz de ficarmos chamando funções atrás de funções, sem relação direta entre si, nós passemos a pensar em objetos conversando entre si. Essa ideia de “conversa” vem da expressão técnica troca de mensagens, algo que foi idealizado na linguagem Smalltalk.

A primeira coisa a dizer sobre orientação a objetos é que ela não é um padrão obrigatório, mas sim um modelo recomendado pela organização e produtividade que proporciona. Já lidei com vários amigos e alunos que, antes de mexer com Objective-C, eram programadores em outras linguagens, e por isso eles tinham uma certa dificuldade em “pensar” orientado a objetos.

A ideia é relativamente simples: pensar em códigos como agentes desempenhando tarefas e possuindo suas características bem definidas. Um exemplo clássico (e difícil de evitar) é falar sobre uma pessoa. Imagine que você precisa fazer um código para controlar um grupo de pessoas. Isso quer dizer que um mesmo tipo de código tende a ser usado com certa frequência, e com um estilo bem definido.

Resumindo os diversos pilares de orientação a objetos, posso destacar dois para facilitar esse começo de estudo: reutilização e herança.

A ideia de reutilização baseia-se na prática de criar um código que vai ser reaproveitado, em tese é a proposta do que tivemos com funções, só que no caso de classes o reaproveitamento é mais amplo e completo.

Voltando ao nosso exemplo do grupo de pessoas, a primeira coisa a fazer é trazer para o código os elementos que o seu sistema precisará lidar. Essa tarefa tem nome: abstração. Abstração nesse contexto quer dizer extrair o que é essencial, e tendo em mente que uma pessoa pode ter muita informação sobre si mesma, separar o essencial é mais do que necessário.

Essa abstração tem que levar em conta o que o sistema precisa saber e manipular. Vamos supor que o nosso sistema vai ser uma lista básica de contatos, então tenham em mente as informações de nome, sobrenome, endereço e telefone. Analisando essas informações nós percebemos que todas são características de uma pessoa, isso quer dizer um estado dessa pessoa. O termo técnico para isso é propriedade.

Cada uma dessas características que separamos vão virar propriedades na classe pessoa. Pensando assim estamos descrevendo o que uma pessoa precisa ter para o nosso sistema funcionar e isso nos permite definir uma classe como um modelo de dados.

Essa é uma definição bem legal que costuma ajudar os iniciantes. Pensem em uma classe como uma receita de bolo, algo que descreve o que determinada coisa possui e pode fazer. E essa noção de receita de bolo explica o pilar da reutilização. Uma vez que uma classe descreve o modelo de dados para uma pessoa, todo o grupo de pessoas já tem um modelo a seguir, logo, tanto para fazer uma pessoa quanto para fazer dez, o trabalho é bem reduzido já que o modelo é o mesmo para todos. O que vai mudar é o valor das propriedades que nossos objetos vão ter (isso ficará claro no final do artigo).

O pilar da herança também pode ser explicado com um exemplo de pessoas. Imagine agora que o seu sistema precise lidar com funcionários e chefes em um controle de pessoal da sua empresa. A premissa da herança é que uma classe possa herdar outra e com isso aproveitar tudo o que já foi definido. A proposta é simples e extremamente útil. Pense que você acabou de gastar um tempo planejando e definindo a classe Funcionário. Escreveu o código com as propriedades nome, sobrenome, idade, número de registro e data de admissão, por exemplo. E agora precisa criar a classe Chefe, mas percebe que ela vai usar tudo o que você declarou no Funcionário, adicionando apenas algumas coisas. Essa situação clama pelo uso da herança. Ao programar a classe Chefe você define no código que ela herda Funcionário e assim automaticamente pode usar tudo o que foi criado previamente. Muito prático, né?!

Passada essa parte dos conceitos essenciais para uma compreensão geral da coisa, vamos começar com a prática — em que eu vou amarrar mais dicas e conceitos.

Prática

Para este estudo de Objective-C nós vamos continuar usando o template de projeto Command Line Tool, mas desta vez quando definir o nome do projeto como Trabalho_Classe você deverá alterar o tipo para Foundation.

Tipo de projeto no Xcode

Ao salvar o seu projeto como habitual, o Xcode irá carregar no Editor o conteúdo do seu arquivo main. Só que vocês perceberão duas diferenças significativas, desta vez:

  1. O tipo do arquivo main em um projeto Foundation é .m e não .c. Essa já é uma forma de saber se um projeto está usando código Objective-C ou não. Foi por isso que alteramos o tipo do projeto para Foundation, para que ele tivesse acesso aos códigos de Obj-C.
  2. Analisando o conteúdo do arquivo main.m, nós notamos uma estrutura bem parecida com o que temos visto nos textos de C, porém algumas mudanças são notórias. Logo no começo você perceberá que não há mais um include para o stdio, mas sim um import para Foundation que é a base de todo programa em Obj-C.

As outras diferenças bem claras são o uso da estrutura autorelease que trabalha com gerenciamento de memória e a função NSLog no lugar da printf. O trabalho com gerenciamento de memória era uma das coisas mais chatas de se fazer anos atrás e consequentemente a principal área de erros de códigos e fechamentos de apps. Se você usa iPhone há pelo menos três anos, é bem provável que já deva ter clicado em um botão de um aplicativo, e ele misteriosamente fechar sem dar explicação. Quase sempre era algum vacilo do programador gerando os chamados memory leaks.

Mas boa parte desses problemas são parte do passado. Como digo para meus alunos na Quaddro, em todas as turmas: “Vocês estão começando na época certa.” Tivemos muitas melhorias no SDK de 2009 para cá, o que tornou a programação mais produtiva e menos suscetível a esses erros bobos. O controle de memória agora é em grande parte gerenciado pelo compilador, desde que na hora de criar o projeto nós deixemos habilitada a opção “Use Automatic Reference Counting”.

Não vou detalhar o chamando memory management agora, até porque o nosso texto é sobre orientação a objetos, então uma vez que tenhamos analisado as diferenças mais significativas desse projeto de Obj-C, vamos criar uma classe para ver tudo o que eu falei na prática.

Para criar uma classe em Obj-C o ideal é gerar um arquivo dedicado a ela, então nós vamos criar um novo arquivo dentro do projeto atual. Para isso você pode usar a opção File » New » File no menu, ou então o atalho Command + N. Escolham o grupo à esquerda iOS » Cocoa Touch e o template Objective-C class.

Novo arquivo em Obj-C no Xcode

Vamos chamar a nossa classe de Funcionario (devemos nomear classes com inicial maiúscula) e como sub-classe de NSObject que já deve ser a opção definida. Ao confirmar e salvar a classe, o Xcode a carregará no Editor e novamente temos duas percepções logo de cara:

  1. Foram listados dois novos arquivos no Navigator, um .h e um .m. Toda classe definida em arquivo próprio é gerado com um arquivo para declarações (.h) e um arquivo para implementações (.m). Lembram que no artigo passado eu primeiro defini o protótipo da função para depois implementá-la?! Foi exatamente para alinhar aquela ideia com esse momento. O protótipo só declara nome e tipos, na classe essa declaração vai no arquivo .h. Já a parte de implementação do que a função faz vai no arquivo .m, que efetivamente define o que vai acontecer. Então pensem sempre assim: arquivo .h declara coisas que vão ser implementadas e usadas no .m.
  2. A segunda percepção clara é o uso de comandos com arroba dentro desses arquivos. Esse uso representa elementos orientados a objeto. É mais ou menos como dizer que algo com arroba é de Objective-C e algo sem arroba é de C básico.

Um exemplo simples mas que deve deixar isso claro é o texto do NSLog. O printf que usamos até agora exibia mensagens em C, já o NSLog exibe mensagens em Obj-C. Mas como o sistema vai saber o que é texto de C e o que é texto de Obj-C? Pelo arroba, oras! Notem que a string (o conteúdo entre aspas) desse NSLog está com um arroba na frente, isso quer dizer que o texto é de Obj-C.

Com essas novas noções podemos trabalhar a nossa classe. A primeira coisa é declarar o que vamos ter, então se falamos em declarar vamos para o arquivo .h. Nele eu vou declarar algumas propriedades (que de maneira minimalista seriam variáveis). A imagem a seguir demonstra o que foi criado no arquivo Funcionario.h:

Declarando arquivo Foundation.h no Xcode

É difícil conseguir transmitir todos os conceitos que ensino nas aulas da Quaddro nestes artigos de blog, até pela quantidade de texto que isso geraria, mas vou tentar sempre explicar os pontos-chave de cada código.

De maneira bem simplista, nesse caso o uso do atributo strong demonstra que a propriedade é orientada a objeto e o assign demonstra uma propriedade primitiva (um tipo básico de C). Nosso código então tem duas propriedades de texto orientado a objeto e uma propriedade básica de número.

Declaradas as propriedades no .h, nós já podemos fazer alguns testes com ela. Acesse agora o arquivo main.m e, para que nós possamos usar a nossa classe, é preciso importá-la. Após a importação eu vou gerar um objeto da nossa classe. Isso quer dizer que vou criar uma variável que, por ser do mesmo tipo da classe, vai carregar tudo o que nós definimos no arquivo .h da classe.

A imagem a seguir ilustra como deve ter ficado o arquivo main.m:

Criando objetos no Xcode

De maneira resumida, os colchetes são a forma de executar funções em Obj-C. No contexto de programação orientada a objetos usamos o termo métodos. O método alloc é responsável por alocar a memória que será usada pela variável, dessa forma ele é necessariamente o começo do processo. Já o init é um método que “inicializa” a variável, verificando se há erros e se ela está disponível para o uso.

Esse é um dos momentos de caretas de programadores de outras plataformas. Tanto pelos colchetes quanto pela alocação explícita de memória, mas fiquem tranquilos que além de fácil essa abordagem é padrão, então será usada sempre o que facilitará sua compreensão.

A partir dessa linha nós temos uma variável (usamos o termo objeto) pronta para uso e contendo tudo o que foi declarado na classe Funcionario. No nosso caso definimos algumas propriedades que podem ser usadas aqui. Para acessar as propriedades nós usamos a chamada sintaxe de pontos após o nome do objeto. Na imagem a seguir eu defino algumas propriedades e exibo o valor de uma delas:

Definindo propriedades no Xcode

A ideia do uso de classe é definir um modelo de dados. Pois bem, se no modelo tínhamos nome, sobrenome e numeroRegistro, no objeto teremos os mesmos itens. Por isso defini um valor para cada propriedade e depois exibi um desses valores.

Notem que, como queremos exibir uma mensagem que use elementos de Obj-C, temos de usar o NSLog no lugar do print básico de C. Ênfase para o arroba antes das aspas para dizer que essa string é de Obj-C, e notem que o especificador de formato aqui é %@, pois estamos querendo mostrar o valor da propriedade nome, que foi declarada como NSString. Esse é um ponto legal e que ajuda os iniciantes: tudo o que for orientado a objetos (tecnicamente o que for declarado com strong) usará o especificador de formato %@.

Uma vez que nós tenhamos o modelo definido na classe, podemos criar diversos objetos a partir dela. Por exemplo, a imagem a seguir demonstra a criação de outro objeto Funcionario, contendo valores e informações diferentes do primeiro, mas tendo como ponto de partida o mesmo modelo de dados descrito no classe:

Declarando outra classe no Xcode

Percebam que eu escolhi por exibir o valor do numeroRegistro do funcionario2, e como ele é um número inteiro poderia usar o especificador de formato %i. Mas a declaração dele foi como um NSInteger que é um tipo de número com maior capacidade de armazenamento do que um simples int, por isso o próprio Xcode sugere a alteração para o especificador de formato %li nesse caso.

A criação desse segundo funcionário demonstra bem o uso de classe e sua vantagem de reutilização. Uma vez que o modelo de dados foi feito no arquivo Funcionario.h, todas as variáveis desse tipo terão automaticamente os mesmos recursos. Então fazer diversas variáveis com o mesmo modelo de dados é fácil.

Como esse texto já ficou grande, vou deixar para o próximo artigo a explicação de métodos e por consequência o uso do arquivo .m da nossa classe.

Espero que tenham pego a ideia de orientação e que essa pequena prática tenha ajudado a entender como a coisa funciona. Em caso de dúvidas ou debates sobre o assunto, postem no tópico oficial no fórum da Quaddro.

Um abraço e até a próxima.

Posts relacionados

Comentários