Programando com Objective-C Parte 1

 

Programando Com Objective-C

Classes

Quando você escreve um programa para OS X ou IOS, a maior parte do seu tempo será usada para trabalhar com objetos. Objetos em Objective-C são apenas como objetos em outras linguagens de programação: eles empacotam informações com comportamento relacionado.

Um app é uma construção como um grande ecossistema de objetos interconectados que se comunicam cada um com outro para resolver um problema específico, tal como uma exibição de interface visual, respondendo a entradas do usuário, ou armazenando informações.

 

Classes são Modelos para Objetos

Uma classe descreve o comportamento e propriedades comuns para qualquer tipo de objeto em particular. Para um objeto string (em Objective-C, isto é uma instancia da classe NSString), a classe oferece vários modo de examinar e converter o caracter interno que é representado. Similarmente, a classe usada para descrever um objeto número (NSNumber) oferece funcionalidades em torno do valor numérico interno, como converter esse valor para um tipo numérico diferente.

Do mesmo modo que várias prédios são construídos com o mesmo diagrama são idênticos em estrutura, toda instância de uma classe divide as mesmas propriedades em comportamentos como todas as outras instâncias da mesma classe. Todas as instâncias de NSString se comportam do mesmo modo, independentemente da cadeia interna de caracteres que ela contenha.

Em Objective-C, a class interface especifica exatamente como um determinado tipo de objecto se destina a ser utilizado por outros objectos. Em outras palavras, ela define uma interface pública entre as instancias da classe e o mundo exterior.

Mutabilidade Determina Se um Valor Representado pode ser Mudado

Algumas clases definem objetos que são imutáveis. Isto significa que o conteúdo interno deve ser determinado quando o objeto é criado, e que não será subsequentemente alterado por outro objeto. Em Objective-C, todos os objetos NSString e NSNumber são imutáveis. Se você precisar representar um número diferente, você deverá usar uma nova instância de NSNumber.

Algumas classes imutáveis também oferecem uma versão mutável. Se você precisar mudar especialmente o conteúdo de uma string em runtime (tempo de execução), por exemplo acrescentar caracteres que são recebidos através de uma conexão de rede, você pode usar uma instância da classe NSMutableString. Instâncias desta classe trabalham como objetos NSString, exceto que eles também oferecem funcionalidades para mudar o caracter que o objeto representa.

Contudo NSString e NSMutableString são classes diferente, elas possuem alguma similaridades. Ao invés de escrever duas classes completamente distintas do zero, que só irão ter alguns comportamentos similares, faz todo o sentido fazer uso de HERANÇA.

Classes Herdam de outras Classes

No mundo natural, a taxonomia classifica animais em grupos usando termos como espécie, gênero, e família. Estes grupos são hierárquicos, tal como múltiplas espécies podem pertencer a um gênero e múltiplos gêneros podem ser uma família.

Gorilas, humanos e orangotangos por exemplo, possuem numerosas similaridades. Embora cada um deles pertencem a diferentes espécies, e mesmo diferentes géneros, tribos e subfamílias, são taxonomicamente relacionadas uma vez que todos eles pertencem à mesma família (chamado “Hominídeos”), como mostrado na Figura 1-1.

No mundo da programação orientada a objeto, objetos também são categorizados em grupos hierárquicos. Ao invés de usar termos diferentes para os diferentes níveis hierárquicos, tais como gênero ou espécie, os objetos são simplesmente organizados em classes. Da mesma forma que os seres humanos herdam certas características, como membros da família Hominídeos, uma classe pode ser definida para herdar a funcionalidade de uma classe pai.

Quando uma classe herda de outra, a filha herda todos os comportamentos e propriedades definidas na classe pai. Ele também tem a oportunidade tanto para definir o seu próprio comportamento e propriedades adicionais ou substituir o comportamento do pai.

No caso da classes string em Obj-C, a descrição da classe para NSMutableString especifica que a classe herda de NSString, como mostra a figura abaixo.

Todas as funcionalidades fornecidas por NSString estão disponível em NSMutableString, como consultas de caracteres específicos ou requisitar novas strings com caixa-alta, mas NSMutableString acrecenta métodos que permitem você adicionar, inserir, substituir ou deleter substrings e caracteres individuais.

  A Classe Raiz Fornece Funcionalidades Básicas

Do mesmo modo que todos os organismos vivos compartilham algumas características básicas de “vida”, algumas funcionalidades são comuns a todos os objetos em Objective-C. Quando um objeto precisa trabalhar com uma instancia de outra classe, é experado que a outra classe ofereça certas características e comportamentos básico. Por este motivo, é definido uma classe raiz a partir da qual a vasta maioria de outras classes herdam, ela é chamada de NSObject.

Quando um objeto encontra outro objeto, é esperado que seja capaz de interagir utilizando, pelo menos, o comportamento básico definido pela descrição da classe NSObject.

Quando se está definindo sua própria classe, você no mínimo deve herdar de NSObject. Em geral, você deverá encontrar um objeto Cocoa ou Cocoa Touch que ofereça as funcionalidades mais próximas que você precise para herdar a partir dela.

Se você quiser definir um botão personalizado para uso em um aplicativo iOS, por exemplo, e a classe UIButton fornecida não oferecem suficientes atributos personalizáveis ​​para satisfazer as suas necessidades, faz mais sentido criar uma nova classe herdada de UIButton que de NSObject. Se voce simplesmente herdar de NSObject, você irá precisar duplicar toda interação visual complexa e comunicação definida na classe UIButton somente para fazer seu botão comportar-se de modo esperado pelo usuário. Além disso, herdando de UIButton, sua subclasse automaticamente ganhará sua subclasse ganha automaticamente quaisquer futuras melhorias ou correções de bugs que podem ser aplicadas ao comportamento interno de UIButton.

A classe UIButton em si é definido para herdar UIControl, que descreve o comportamento básico comum a todos os controles de interface de usuário no iOS. A classe UIControl por sua vez herda de UIView, dando-lhe funcionalidades comuns aos objetos que são exibidos na tela. UIView herda UIResponder, permitindo-lhe responder à entrada do usuário, como torneiras, gestos ou shakes. Finalmente, na raiz da árvore, UIResponder herda NSObject, como mostrado na figura abaixo:

Raiz da Classe

Este arranjo de herança significa que qualquer subclasse personalizada de UIButton herdará não somente as funcionalidades declaradas somente em UIButton, mas também herdará funcionalidades de cada uma superclasse por sua vez. Deste modo você terminará com uma classe para um objeto que se comporta como um botão, que poderá se exibir na tela, responde a entradas do usuário, e comunica com qualquer outro objeto básico de Cocoa Touch.

Por isso é importante manter a cadeia de herança em mente, para qualquer classe que você precisar usar, a fim de que possa funcionar exatamente da maneira que se quer. A documentação de referência a classe fornecida para o Cocoa e o Cocoa Touch, por exemplo, permite uma navegação fácil a partir de qualquer classe para cada uma de suas superclasses. Se você não consegue encontrar o que você está procurando em uma interface de classe ou de referência, que pode muito bem ser definido e documentado em mais uma superclasse na cadeia.

  A Interface de uma Classe Define Interações Esperadas

Um dos muitos benefícios da programação orientada a objetos é a idéia mencionada acima – tudo que você precisa saber para usar uma classe é como interagir com suas instâncias.Mais especificamente, um objeto deve ser projetado para esconder os detalhes de sua implementação interna.

Se você usa um UIButton padrão em algum aplicativo IOS por exemplo, você não precisa se preocupar sobre como os pixels são manipulados para que o botão apareça na tela. Tudo que você precisa saber é oque você pode mudar certos atributos, como os títulos e as cores dos botões, e confiar que quando você adicioná-lo à sua interface visual, ele será exibido corretamente e se comportar da maneira que você espera.

Quando você está definindo sua própria classe, é preciso começar por descobrir esses atributos públicos e comportamentos.Que atributos você quer acessível ao público? Se você quer permitir que esses atributos sejam alterados? Como outros objetos se comunicam com instâncias de sua classe?

Esta informação vai para a interface da sua classe – que define a maneira como você pretende que outros objetos interajam com as instâncias de sua classe. A interface pública é descrito separadamente do comportamento interno de sua classe, o que se torna a implementação da classe. Em Objective-C, a interface e a implementação são geralmente colocados em arquivos separados, para que você só precisa fazer a interface pública.

Sintaxe Básica

A sintaxe básica para declarar uma class interface (interface de classe) é parecido com isso:

@interface SimpleClass : NSObject

@end

Este exemplo declara uma calsse chamada de SimpleClass, que herda da classe NSObject.

As propriedades públicas e comportamentos são definidos dentro da declaração @interface. Neste exemplo, nada é especificado além da superclasse, então as únicas funcionalidades que deverão estar disponíveis na instância de SimpleClass são as funcionalidades herdadas de NSObject.
Controle de Acesso as Propriedades para Valores de um Objeto.

Objetos freqüentemente possuem propriedades destinadas para acesso público. Se você definir uma classe para representar um ser humano em um aplicativo que Capta-Mantêm informações, por exemplo, você poderá decidir se precisa de prorpriedades para strings representando o primeiro e último nome de uma pessoa.

Declaração para estas propriedades deverão ser adicionadas dentro da interface, como a seguir:

@interface Person: NSObject

@property NSString *firstName;

@property NSString *LastName;

@end

Neste exemplo, a classe Person declara duas propriedades públicas, ambas as quais são instancias da classe NSString.

Essas duas propriedades são para Objective-C objetos, então elas usam um asterisco para indicar que elas são ponteiro C. Eles também são declarações como qualquer outra declaração de variável em C, e portanto, necessitam de um ponto e vírgula no final.

Você pode decidir adicionar propriedades para representar o ano de nascimento de uma pessoa permitindo que voce organize as pessoas em grupos através da idade e não somente pelo nome. Você irá usar uma propriedade para objetos de números:

@property NSNumber *yearOfBirth;

Mas isto pode ser considerado exagerado apenas para armazenar um valor numérico simples. Uma alternativa seria a utilização de um dos tipos primitivos fornecidas por C, que possuem valor escalar, tais como um número inteiro:

@property int yearOfBirth;

Atributos de Propriedade Indicam Acessibilidade e Considerações Sobre Armazenamento de Dados.

Nos exemplos mostrados até agora todas as propriedades declaradas são destinados ao acesso público completo. Isto siginifica que outros objetos podem ambos, ler e mudar, o valor das propriedades.

Em alguns casos, você pode decidir declarar que uma propriedade não se destina a ser alterada. No mundo real, uma pessoa deve preencher uma grande quantidade de papelada para mudar o seu nome ou sobrenome documentado.Se você estivesse escrevendo um um aplicativo oficial que Capta-Mantêm informações de uma pessoa, você pode escolher que as propriedades públicas para o nome de uma pessoa sejam especificadas como somente leitura, exigindo que qualquer alteração será solicitada através de um objeto intermediário responsável por validar o pedido, aprovando ou negando.

Declarações de propriedades de Objective-C podem incluir atributos de propriedades, que são usadas para indicar, entre outras coisas, se uma propriedade é destinada a ser somente leitura(read-only). Em um aplicativo oficial que Capta-Mantêm informações de uma pessoa, a interface da classe Person poderá ficar semelhante a isto:

@interface Person : NSObjet

@property (readonly) NSString *firstName;

@property (readonly) NSString *lastName;

@end

Atributos de propriedades são especificadas dentro de parenteses depois da palavra chave @property, e são totalmente descritas em “Declare Public Properties for Exposed Data.”

Declarações de Método Indicam as Mensagens que um Objeto Pode Receber

Os exemplos até agora envolveram uma classe que descreve um típico modelo de objeto, ou um objeto projetado principalmente para encapsular dados. No caso de uma classe Person, é possível que não seria necessário qualquer funcionalidade além de ser capaz de acessar as duas propriedades declaradas. A maioria das classes, no entanto, não incluem comportamento para além de quaisquer propriedades declaradas.

Dado que o software feito em Objective-C é construído a partir de uma grande rede de objetos, é importante notar que esses objetos podem interagir uns com os outros através do envio de mensagens. Em termos de Objective-C, um objeto envia uma mensagem para outro objeto chamando um método no objeto.

Métodos Objective-C são conceitualmente similares as funções padrões em C e outras linguagens de programação, embora a sintaxe seja um pouco diferente. Uma declaração de função em C é semelhante a isso:

void SomeFunction();

A declaração equivalente do método em Obj-C é semelhante a isso:

– (void) someMethod;

Neste cado, o método não possui parâmetros. A palavra chave void de C é usada dentro de parenteses no começo da declaração para indicar que o método não retorna qualquer valor, uma vez que está terminado.

O sinal de menos “ – “ na frente do método indica que se trata de um método da instância, que pode ser chamado em qualquer instância da classe. Isto a diferencia do método da classe, que pode ser chamado somente na classe, como descrito em “Objective-C Classes Are also Objects.”

Tal como protótipos de funções C, uma declaração de método dentro de uma interface de classe em Obj-C é apenas como qualquer declaração C e requer a terminação com ponto-virgula “ ; ”.

Métodos Podem Ter Parâmetros

Se você precisa declarar um método que tenha um ou mais parâmetros, a sintaxe é muito diferente das típicas funções de C.

Para uma função C, os parâmetros são especificados dentro dos parenteses, como visto abaixo:

void SomeFunction(SomeType value);

Uma declaração de métodos Obj-C inclui os parametros como parte do nome, usando dois-pontos “ : “, como visto abaixo:

– (void) someMethodWithValue: (SomeType) value;

Tal como acontece com o tipo de retorno, o tipo de parâmetro é especificado entre parênteses, como uma padrão conversão de tipo(cast-type) realizado em C.

Se você precisa fornecer vários parâmetros, a sintaxe é novamente bastante diferente do C. Vários parâmetros para uma função C são especificados dentro dos parênteses, separados por vírgulas, em Objective-C, a declaração de um método que recebe dois parâmetros parece com isso:

– (void) someMethodWithFirstValue:(SomeType) value1 secondValue:(AnotherType) value2;

Neste exemplo, value1 e value2, são os nomes usados na implementação para acessar o valores fornecidos quando o método é chamado, como se fossem variáveis.

Algumas linguagens de programação permitem definições de funções com os chamados argumentos nomeados; É importante notar que este não é o caso em Objective-C. A ordem dos parâmetros em uma chamada de método deve ser igual a declaração do método, e na verdade o SecondValue: é parte da declaração do método, sendo parte do nome do método:

someMethodWithFirstValue: secondValue:

Esta é uma das características que ajudam a torná Objective-C uma língua tão legível, porque os valores transmitidos por uma chamada de método são especificados na linha, ao lado da parte relevante do nome do método, conforme descrito em “You Can Pass Objects for Method Parameters.”

****************************************

Nota: Os nomes dos valores valor1 e valor2 usados ​​acima não são estritamente parte da declaração do método, o que significa que não é necessário usar exatamente os mesmos nomes dos valores na declaração como você faz na implementação. O único requisito é que as assinaturas se correspondam, o que significa que é necessário manter o nome do método, bem como o parâmetro e tipos de retorno exactamente o mesmo.

Como um exemplo, este método tem a mesma assinatura como a mostrada acima:

-(void) someMethodWithFirstValue: (SomeType) info1 secondValue: (AnotherType) info2;

Estes métodos têm as assinaturas diferentes para aquele acima:

– (void) someMethodWithFirstValue: (SomeType) info1 anotherValue:(AnotherType) info2;

– (void) someMethodWithFirstValue: (SomeType) info1 secondValue: (YetAnotherType) info2;

**************************************************

Nome de Classes Devem Ser Únicos

É importante destacar que o nome da cada classe dever ser único dentro de um aplicativo, em toda a biblioteca incluída e frameworks. Se você tentar criar uma nova classe que tenha o mesmo nome de uma classe ja existente no projeto, você irá receber um erro de compilação.

Por esta razão, é aconselhável prefixar os nomes de todas as classes que você define, por meio de três ou mais letras. Estas letras podem estar relacionadas ao aplicativo que você está escrevendo no momento, ou o nome de um framework de código reutilizável, ou talvez apenas suas iniciais.

Todos os exemplos dados no restante deste documento usam nome de classe prefixada, como este exemplo:

@interface XYZPerson : NSObject

@property (readonly) NSString *firtName;

@property (readonly) NSString *lastName;

@end

Nomes de métodos e propriedades, pelo contrário, só precisam ser exclusivos dentro da classe em que são definidos.

Nomes de métodos e propriedades, pelo contrário, só precisam ser exclusivos dentro da classe em que são definidos. Embora cada função C em um aplicativo deve ter um nome único, é perfeitamente aceitável (e muitas vezes desejável) para várias classes Objective-C, definir métodos com o mesmo nome. Você não pode definir um método mais de uma vez na mesma declaração de classe, no entanto, embora se você deseja substituir um método herdado de uma classe pai, você deve usar o nome exato usado na declaração original.

Tal como acontece com os métodos, as propriedades de um objeto e variáveis ​​de instância (descrito em “Most Properties Are Backed by Instance Variables”) precisam ser exclusivas apenas dentro da classe em que são definidos. Se você faz uso de variáveis ​​globais, no entanto, estes devem ser nomeados exclusivamente dentro de um aplicativo ou projeto.

Outras convenções de nomenclatura e sugestões são dadas em “Conventions.”

A implementação de uma classe fornece seu comportamento interno.

Depois de definir a interface de uma classe, incluindo as propriedades e métodos destinados ao acesso público, você precisa escrever o código para implementar o comportamento da classe.

Como dito acima, a interface para uma classe é usualmente colocada dentro de um arquivo dedicado, freqüentemente referenciado como arquivo de cabeçalho (header file), que geralmente possui a extensão “ .h “ . Você escreve a implementação para a classe dentro do arquivo de código fonte com a extensão “ .m “ .

Sempre que a interface é definida em uma header file, você irá precisar dizer ao compilador para o ler antes de tentar compilar a implementação dentro do arquivo de código fonte. Obj-C fornece uma diretiva de pré-processador, #import, para este propósito. É parecido com a diretiva #include de C, mas assegura que um arquivo está incluído apenas uma vez durante a compilação.

Perceba que as diretivas de preprocessador são diferentes das tradicionais declarações de C e não usam o ponto-vígula no final.

#import “XYZPerson.h”

@implementation XYZPerson

@end

Se você declara qualquer método na interface da classe, você irá precisar implementá-la dentro deste arquivo.

Implementando Métodos

Para uma simples inferface de classe com um método, como este:

@interface XYZPerson : NSObject

-(void) sayHello;

@end

A implementação poderia ter esta aparência:

#import “XYZPerson.h”

@implementation XYZPerson

-(void) sayHello {

NSLog(@”Hello, World!”);

}

@end

Este exemplo usa a função NSLog para registrar uma menssagem no console. NSLog é muito parecido com a função printf(), e recebe um número variável de parâmetros, o primeiro dos quais deve ser uma string Objective-C.

A implementação de métodos é muito parecida com a definição de funções em C em que são usados o símbolo de chaves “{ }” para conter o código. Além disso, o nome do método deve ser idêntico ao do seu protótipo, e os tipos de parâmetro e de retorno deve corresponder exatamente.

Objective-C herda case sensitivity (diferença de maiúscula e minúscula) de C, de modo que este método:

-(void) sayhello {

}

Seria tratado pelo compilador de modo completamente diferente do que com o método sayHello mostrado anteriormente.

Em geral, os nomes dos métodos devem começar com uma letra minúscula. A convenção Objective-C é usar nomes mais descritivos para métodos do que você pode ter visto usado para funções típicas C. Se um nome de método envolve várias palavras, use caso camelo (colocar maiúscula a primeira letra de cada nova palavra) para torná-los fáceis de ler.

Note também que o espaço em branco é flexível em Objective-C. É costume recuar cada linha dentro de qualquer bloco de código usando tanto tabulações ou espaços, e muitas vezes você vai ver a abertura de a chave esquerda em uma linha separada, como este:

-(void)sayHello

{

NSLog(@”Hello, World!”);

}

Você vai ver muitos mais exemplos de implementações do método no próximo capítulo “Trabalhando com Objetos.” 

Classes Objective-C também são Objetos

Em Objective-C, uma classe é um objeto de um tipo opaco chamada de classe. Classes não podem ter propriedades definidas usando a sintaxe de declaração mostrada anteriormente para instâncias, mas eles podem receber mensagens.

O uso típico para um método de classe é como um factory method, que é uma alternativa para a alocação e procedimento de inicialização de objetos descrito em “Objects Are Created Dynamically.”. A classe NSString, por exemplo, possui uma variedade de factory methods disponiveis para criar de um objeto de string vazio, ou um objeto de string inicializado com um caracter específico, incluindo:

+(id) string:

+(id) stringWithString: (NSString *) umaString;

+(id) stringWithFormat: (NSString *) format, …;

+(id) stringWithContentsOfFile: (NSString *) caminho codificação:(NSSStringEncoding) enc error:(NSError **) error;

+(id) stringWithCString: (const char *) cString encoding: (NSStringEncoding)enc;

Como mostrado neste exemplo, os métodos de classe são denotados pelo uso de um sinal “ + ”, que diferencia-os de um método de instância que usam um sinal de “ – ”.

Protótipos de métodos de Classes podem ser incluídos na interface da classe, assim como protótipos de métodos de instancias. Métodos de classe são implementados do mesmo modo que os métodos de instância, dentro do bloco @implementation da classe.

EXERCÍCIOS

1 – Use o Xcode para criar uma nova classe, vá em File>New File e na janela de templates escolha Objective-C Class para criar os arquivos de interface e implementação de uma classe Objective-C. Coloque o nome de XYZPerson, e no campo subclass escolha NSObject.

2 – Adicione propriedades para o primeiro nome de uma pessoa, sobrenome e data de nascimento (as datas são representadas pela classe NSDate) para a interface de classe XYZPerson.

3 – Declare o método sayHello e implementá-lo como mostrado no início do capítulo.

4 – Adicione uma declaração de uma classe factory method, chamada “person”. Não se preocupe em implementar este método até que você ler o próximo capítulo.

Nota: Se você estiver compilando o código, você vai receber um aviso sobre uma “implementação incompleta”, devido a esta implementação em falta.

Trabalhando com Objetos

A maioria do trabalho em um aplicativo Objective-C acontece como resultado de mensagens que são enviadas para trás e para frente através de um ecossistema de objetos. Alguns desses objetos são instâncias de classes fornecidas pelo Cocoa ou Cocoa Touch, alguns são instâncias de suas próprias classes.

O capítulo anterior descreveu a sintaxe para definir a interface e da execução de uma classe, incluindo a sintaxe para implementar métodos que contêm o código a ser executado em resposta a uma mensagem. Este capítulo explica como enviar tal mensagem a um objeto, e inclui a cobertura de algumas das características dinâmicas do Objective-C, incluindo tipagem dinâmica e capacidade para determinar qual método deve ser invocado em tempo de execução.

Antes de um objecto poder ser utilizado, ele deve ser criada usando uma combinação adequada de alocação de memória para as suas propriedades e a inicialização necessária dos seus valores internos. Este capítulo descreve como aninhar as chamadas de método para alocar e inicializar um objeto, a fim de garantir que ele está configurado corretamente.

Objetos Enviam e Recebem Mensagens

Apesar de existirem várias maneiras de enviar mensagens entre objetos em Objective-C, de longe, o mais comum é a sintaxe básica que usa colchetes, assim:

[algumObjeto fazAlgumaCoisa];

A referencia a esquerda, algumObjeto neste caso, é o receptor da mensagem. A mensagem a direita, fazAlgumaCoisa, é o nome do método invocado por este receptor. Em outras palavras, quando a linha de cima do código é executada, algumObjeto está enviado a mensagem doSomething.

O capítulo anterior descreveu como criar a interface de uma classe, como este:

@interface XYZPerson : NSObject

-(void)digaOla;

@end

E como criar a implementação desta classe, deste modo:

@implementation XYZPerson

– (void) digaOla

{

NSLog(@”Ola mundo!”);

}

@end

Nota: Este exemplo usa uma string Objective-C literal, @ “Olá, mundo!”. Strings é uma dos vários tipos de classe em Objective-C, que permitem uma abreviada sintaxe literal para a sua criação. Especificando @ “Olá, mundo!” é conceitualmente equivalente a dizer “Um objeto string de Obj-C que representa uma string “Olá mundo!”.”

Literais e criação de objetos são explicados mais em “Objects Are Created Dynamically,” depois deste capítulo.

Assumindo que você tenha guardado o objeto XYZPerson, supondo que você tenha posse de um objeto XYZPerson, você pode enviar a ele a mensagem digaOla assim:

[algumaPessoa digaOla];

Enviar uma mensagem de Objective-C é muito conceitual como chamar uma função C. A Figura abaixo mostra o fluxo do programa efetivo para a mensagem digaOla.

De modo a especificar o receptor de uma mensagem, é importante entender como os ponteiros são usados ​​para se referir a objetos em Objective-C.

Usar Ponteiros Para Controlar Objetos

C e Objective-C usar variáveis ​​para controlar valores, assim como a maioria das outras linguagens de programação.

Existe um número de tipos básicos de variáveis ​​escalares definidas conforme a norma C, incluindo inteiros, números de ponto flutuante e os caracteres, que são declarados e atribuído valores, assim como este exemplo:

int algumInteiro = 42;

float algumNumeroDePontoFlututante = 23.14f;

As variáveis ​​locais, que são variáveis ​​declaradas dentro de um método ou função, são como neste exemplo:

-(void) meuMetodo {

int algumInteiro = 42;

}

São limitadas dentro do escopo do método em que são definidos. Ou seja, não podem ser acessadas por outros métodos e são apenas para uso do método onde foram criadas.

Neste exemplo, algumInteiro é declarado como uma variavel local dentro de meuMetodo; uma vez que a execução atinge a chave de fechamento do método, someInteger não será mais acessível. Quando uma variável escalar local (como um int ou float) vai embora, o valor desaparece também.

Objetos Objective-C, pelo contrário, são alocados de forma ligeiramente diferente. Objetos normalmente têm uma vida mais longa do que o simples escopo de uma chamada de método. Em particular, um objeto muitas vezes precisa ficar vivo por mais tempo do que a variável original que foi criado para acompanhar, então a memória de um objeto é alocado e desalocado dinamicamente.

Nota: Se você está acostumado a usar termos como a pilha eo heap, uma variável local é alocado na pilha, enquanto os objetos são alocados na heap.

Isso requer o uso de ponteiros C (que possuem endereços de memória) para controlar sua localização na memória, como neste exemplo:

– (void) meuMetodo {

NSString *minhaString = // Recebe uma string de algum lugar…

[…]

}

Embora o escopo do ponteiro da variável minhaString ( o asterísco indica que é um ponteiro ) é limitado ao âmbito do método meuMetodo, o verdadeiro objeto string que ele aponta, na memória, pode ter uma vida mais longa fora deste escopo. Ele já poderia existir, ou talvez você precise passar o objeto por chamadas de métodos adicionais por exemplo.

Você Pode Passar Objetos para Parâmetros de Métodos

Se você precisar repassar um objeto ao enviar uma mensagem, você fornece um ponteiro de objeto para um dos parâmetros do método. O capítulo anterior descreveu a sintaxe para declarar um método com um único parâmetro:

– (void) algumMetodoComValor: (AlgumTipo) valor;

– (void)someMethodWithValue:(SomeType)value;//Mesmo método versão de original

A sintaxe para declarar um método que recebe um objeto string, portanto, veja no exemplo:

– (void) digaAlgo: (NSString *) saudacao;

Voce pode implementar o metodo digaAlgo: deste modo:

-(void) digaAlgo:(NSString *)saudacao {

NSLog(@”%@”, saudacao);

}

O ponteiro saudacao se comporta como uma variável local e é de alcance limitado apenas ao método digaAlgo: , mesmo que o objeto string que ele aponta existia antes do método que está sendo chamado, e vai continuar a existir depois que o método seja concluído.

Nota: NSLog () usa especificadores de formato para indicar sinais de substituição, assim como a biblioteca padrão C para a função printf (). A string registrada para o console é o resultado da modificação no formato da string (o primeiro argumento), inserindo os valores fornecidos (os argumentos restantes).

Há um sinal adicional de substituição disponível em Objective-C,% @, usado para denotar um objeto. Em tempo de execução, este especificador será substituído com o resultado da chamada ou o método descriptionWithLocale:, (se existir) ou o método de descrição sobre o objeto fornecido. O método de descrição é implementado por NSObject para retornar a classe e endereço de memória do objeto, mas muitas classes Cocoa e Cocoa Touch o substitua para fornecer informações mais úteis. No caso de NSString, o método descrição, simplesmente devolve a cadeia de caracteres que ela representa.

Para mais informações sobre os especificadores de formato disponíveis para uso com as classes NSLog() e NSString veja “String Format Specifiers”.

Métodos Podem Retornar Valores

Bem como passar valores através de parâmetros de método, é possível para um método retornar um valor. Cada método descrito neste capítulo até agora tem um tipo de retorno void (nulo) . A palavra chave void de C significa que um método não retorna valor.

Especificando um tipo de retorno int significa que o método retorna um valor escalar inteiro:

-(int) numeroMagico;

A implementação do método usa a declaração return de C para indicar o valor que será passado de volta depois que o método tiver terminado sua execução, como no exemplo:

– (int) numeroMagico {

return 42;

}

É perfeitamente aceitável ignorar o fato de que um método retorna um valor. Neste caso, o método MagicNumber não faz nada de útil exceto retorna um valor, mas não há nada de errado com a chamada do método como este:

[algumObjeto numeroMagico];

Se você precisa manter o controle do valor retornado, você pode declarar uma variável e atribuir a ele o resultado da chamada do método, assim:

int numeroInteressante = [algumObjeto numeroMagico];

Você pode retornar objetos de métodos exatamente da mesma maneira. A classe NSString, por exemplo, oferece um método uppercaseString:

– (NSString *) uppercaseString;

Ele é usado da mesma forma como um método retornando um valor escalar, mas você precisa usar um ponteiro para acompanhar o resultado:

NSString *testeString = @”Olá mundo!”;

NSString *revisadaString = [testeString uppercaseString];

Quando esse método chamado retorna, revisedString irá apontar para um objeto NSString que representa os caracteres OLÁ MUNDO!. Ou seja, ele irá retornar o valor contido no objeto testeString com uma nova formatação, colocando toda a sentença em uppercase (maiúsculo).

Lembre-se que quando a implementação de um método para retornar um objeto, assim:

-(NSString *) magicaString {

NSString *stringToReturn = // criar uma string do seu interesse…
return stringToReturn;

}

O objeto string continua a existir quando ele é passado como um valor de retorno, mesmo que o ponteiro stringToReturn sai do escopo.

Há algumas considerações gerenciamento de memória nessa situação: um objecto devolvido (criado na pilha) precisa existir tempo suficiente para que possa ser usado pelo chamador original do método, mas não perpetuamente porque isso criaria uma perda de memória. Para a maior parte, o recurso automático de contagem de referência (ARC – Automatic Reference Count) do compilador Objective-C cuida dessas considerações para você.

Objetos Podem Enviar Mensagens para Eles Mesmos

Sempre que você está escrevendo uma implementação do método, você tem acesso a um importante valor oculto, self. Conceitualmente, self é uma maneira de se referir ao “o objeto que recebeu esta mensagem.” É um ponteiro, assim como o valor saudação acima, e pode ser usado para chamar um método no objeto receptor atual.

Você pode optar por refatorar a implementação de XYZPerson modificando o método digaOla e usar o método digaAlgo: como mostrado acima, movendo assim a chamada NSLog () para um método separado. Isso significa que você pode adicionar outros métodos, como digaTchau, que cada chamada até o método digaAlgo: para tratar do processo de saudação atual. Se mais tarde você quiser exibir cada método saudacao em um campo de texto na interface do usuário, você só precisa modificar o método digaAlgo: ao invés de ter que passar e ajustar cada método saudação individualmente.

A nova implementação usando o self para chamar uma mensagem no objeto atual ficaria assim:

@implementation XYZPerson

-(void)digaOla {

[self digaAlgo:@”Ola mudo!”];

}

– (void) digaAlgo:(NSString *) saudacao {

NSLog(@”%@, greeting);

}

@end

Se você enviou uma mensagem digaOla do objeto XYZPerson para a implementação atualizada, o fluxo efetivo do programa será como mostrado abaixo:

(Leia este documento para melhor entendimento do uso de Self:
https://docs.google.com/document/d/1VMXpm-tvdAkyU45YsHxNMD0kJHfMzzZ5ZmI7xjTzqyA/edit )

Os Objetos Podem Chamar Métodos Implementados Pelas suas Superclasses

Existe outra palavra-chave importante disponível para você em Objective-C, chamada super. Enviar uma mensagem a super é uma maneira de chamar através de uma implementação do método, definido por uma superclasse, ainda mais cima na cadeia de herança. O uso mais comum de super é quando substituímos um método.

Digamos que você queira criar um novo tipo de classe de pessoa, uma classe “pessoa gritando”, onde cada saudação é exibido usando letras maiúsculas. Você poderia duplicar toda a classe XYZPerson e modificar cada string em cada método a ser maiúscula, mas a maneira mais simples seria a criação de uma nova classe que herda de XYZPerson, e apenas substituir o método digaAlgo: para que apresente a saudacao em letras maiúsculas, assim:

XYZPessoaGrintando.h

@interface XYZPerson

@end

——————————————-

XYZPessoaGrintando.mn

@implemantation XYZPessoaGrintando

-(void) digaAlgo: (NSString *) saudacao {

NSString *saudacaoMaiuscula = [saudacao stringMaiuscula];

NSLog(@”%@”, saudacaoMaiuscula);

}

@end

Este exemplo declara um ponteiro string extra, saudacaoMaiuscula e atribui ela o valor retornado do envio da mensagem original do objeto saudacao à mensagem stringMaiuscula. Como você viu anteriormente, este será um novo objeto string construído através da conversão de cada caractere na string original em maiúsculas.

Portanto digaAlgo é implementado por XYZPessoa, e XYZPessoaGrintando é determinado pela herança de XYZPessoa. Você pode chamar digaAlgo em um objeto XYZPessoaGrintando também. Quando você chamar digaAlgo em um XYZPessoaGrintando, a chamada para [self saysomething: …] vai usar a implementação substituída e exibir a saudação como maiúsculas, resultando no fluxo do programa mostrado na figura abaixo:

A nova implementação não é o ideal, no entanto, porque se você decidir, mais tarde, modificar a implementação XYZPerson de digaAlgumaCoisa: para exibir a saudacao em um elemento de interface do usuário, em vez de usar NSLog (), você precisará modificar a implementação de XYZShoutingPerson também.

Uma idéia melhor seria mudar a versão XYZShoutingPerson de digaAlgumaCoisa: para chamar até a implementação da superclasse (XYZPerson) para lidar com a saudacao atual:

@implemantation XYZPessoaGritando

– (void) digaAlgumaCoisa: (NSString *)saudacao {

NSString *saudacaoMaiuscula = [saudacao stringMaiuscula];

[super digaAlgumaCoisa: saudacaoMaiuscula];

}

@end

O fluxo efetivo do programa que agora resulta de enviar um objeto XYZShoutingPerson a mensagem digaOla é mostrado na imagem abaixo:

Objetos São Criados Dinamicamente

Conforme descrito no início deste capítulo, a memória é alocada dinamicamente para um objeto Objective-C. O primeiro passo na criação de um objeto para certificar-se

O primeiro passo na criação de um objeto é garantir de que há memória alocada não somente para as propriedades definidas na classe do objeto, mas também as propriedades definidas em cada super classe em sua cadeia de herança.

A classe raiz NSObject fornece um método de classe, de alocação, que lida com esse processo para você:

+(id) alloc;

Observe que o tipo de retorno desse método é id. Esta é uma palavra especial utilizada em Objective-C para significar “algum tipo de objeto.” É um ponteiro para um objeto, como (NSObject *), mas é especial pelo fato de não utiliza um asterisco. É descrito em detalhes mais adiante neste capítulo.

O método alloc tem uma outra tarefa importante, que é para limpar a memória alocada para as propriedades do objeto, definindo para zero. Isso evita o problema usual de memória contendo lixo de tudo o que foi armazenado antes, mas não é suficiente para inicializar um objeto completamente.

Você irá precisar combinar uma chamada para o método alloc com a chamada para init, outro método de NSObject:

-(id) init;

O método init é utilizado por uma classe para certificar-se de suas propriedades têm valores iniciais adequados na criação, e é abordado com mais detalhes no próximo capítulo.

Note-se que init também retorna um id.

Se um método retorna um ponteiro de objeto, é possível aninhar a chamada para esse método como o  receptor de uma chamada para outro método, combinando assim várias chamadas de mensagens em uma declaração. A maneira correta de alocar e inicializar um objeto é aninhar a chamada alloc dentro da chamada para o init, como esta:

NSObject *novoObjeto = [[NSObject alloc] init];

Este exemplo define a variável novoObjeto para apontar para uma instância NSObject recém-criado. (1 )A chamada mais interna é executado primeiro, por isso a classe NSObject é enviada ao método alloc, que retorna uma instância NSObject recém-alocado. (2) Este objeto que está somente alocado e foi devolvido é depois utilizado como receptor da mensagem init, o qual se auto retorna o objeto que é atribuido ao ponteiro novoObjeto, como mostrado na imagem abaixo:

Nota: É possível que o init retorne um objeto diferente do que foi criado pela alocação, por isso a melhor prática para aninhar as chamadas, é como mostrado.

Nunca inicializar um objeto sem reatribuir qualquer ponteiro para esse objeto. Por exemplo, não faça isso:

NSObject *algumObjeto = [NSObjetct alloc];

[algumObjeto init];

Se a chamada para init retornar algum outro objeto, você vai ficar com um ponteiro para o objeto que foi originalmente alocado, mas nunca inicializado.

Métodos Inicializados Podem Ter Argumentos

Alguns objetos precisam ser inicializados com valores necessários. Um objeto NSNumber, por exemplo, deve ser criado com o valor numérico nescessário para representar.

A classe NSNumber define muitos inicializadores, incluindo:

-(id) initWithBool :(BOOL) value;

-(id) initWithFloat: (float) value;

-(id) initWithInt: (int) value;

-(id) initWithLong:(long)value;

Métodos de inicialização com argumentos são chamados do mesmo modo como simples métodos init – um objeto NSNumber é alocado e inicializado como:

NSNumber *numeroMagico = [[NSNumber alloc] initWithInt: 42];

Métodos de Fábrica de Classe( Class Factory) São uma Alternativa Para Alocação e Inicialização

Como mencionado no capítulo anterior, uma classe pode tambem definir métodos “fábrica”. Métodos de Fábrica oferecem uma alternativa do processo tradicional alloc] init], sem a nescessidade de aninhar os dois métodos.

A classe NSNumber define muitas classes métodos fábrica para atender seu inicializadores, incluindo:

+ (NSNumber *) numberWithBool:(BOOL) value;

+ (NSNumber *) numberWithFloat:(float) value;

+ (NSNumber *) numberWithInt:(int)value;

+ (NSNumber *) numberWithLong:(long)value;

Um método de fábrica é usado deste modo?

NSNumber *numeroMagico = [NSNumber numberWithInt:42];

Isto é efetivamente o mesmo como no exemplo anterior usando alloc] initWithInt:]. Isto é efectivamente o mesmo que no exemplo anterior, utilizando alloc] initWithInt:]. Classe Métodos de fábrica geralmente chamam diretamente através alocação eo método init relevante, e são providenciados para sua conveniência.

Use New para Criar um Objeto Se Nenhum Argumento For Necessário Para a Inicialização

Também é possível criar uma instância de uma classe usando o método de classe new. Este método é fornecido por NSObject e não precisa ser substituído em suas próprias subclasses.

É efetivamente o mesmo que chamar alloc e init sem argumentos:

XYZObjeto *objeto = [XYZPessoa new];

// é efetivamente o mesmo que:

XYZObjeto * objeto = [[XYZObjeto alloc] init];
Literais Oferecem uma Sintaxe Objeto-Criação Concisa

Algumas classes permitem que você use uma forma mais concisa, a sintaxe literal para criar instâncias.

Você pode criar uma instância de NSString, por exemplo, usando uma notação literal especial, como este:

NSString *algumaCoisa = @”Ola Mundo!”;

Isto é efectivamente o mesmo que atribuir e inicializar um NSString ou usando um de seus métodos de fábrica de classe:

NSString *algumaCoisa = [NSString

stringWithCString:”Ola Mundo!” encoding: NSUTF8StringEncoding];

A classe NSNumber também permite uma variedade de literais:

NSNumber *meuBOOL = @YES;

NSNumber *meuFloat = @3.14f;

NSNumber *meuInt = @42;

NSNumber *meuLong = @42L;

Mais uma vez, cada um desses exemplos é efetivamente o mesmo que usar o inicializador relevante ou um método de fábrica classe.

Você também pode criar uma NSNumber usando uma expressão “em caixa”, como esta:

NSNumber *meuInt = @(84/2);

Neste caso, a expressão é avaliada, e uma instância NSNumber é criada com o resultado.

Objective-C também suporta literais para criar imutável NSArray e objetos NSDictionary, que são discutidos no “Valores e Coleções.”

Objective-C é uma Linguagem Dinâmica

Como mencionado anteriormente, você precisará usar um ponteiro para manter o controle de um objeto na memória. Por causa da natureza dinâmica de Objective-C , não importa qual tipo de classe específica você usou para um ponteiro – o método correto sempre será chamado no objeto relevante quando você enviar uma mensagem.

O tipo id define um ponteiro de objeto genérico. É possível usar o id ao declarar uma variável, mas você perde as informações de tempo de compilação sobre o objeto.

Considere o seguinte código:

id algumObjeto = @”Ola Mundo”;

[algumObjeto removaTodosObjetos];

Neste caso, algumObjeto irá apontar para uma instância NSString, mas o compilador não sabe nada sobre essa instância além do fato de que é algum tipo de objeto. A mensagem removaTodosObjetos é definido por alguns Cocoa ou Cocoa Touch Objects (como NSMutableArray) para que o compilador não se queixa, mesmo que esse código geraria uma exceção em tempo de execução porque um objeto NSString não pode responder a removaTodosObjetos.

Reescrevendo o código para usar um tipo estático:

NSString *algumObjeto = @”Ola Mundo!”;

[algumObjeto removaTodosObjetos];

significa que o compilador agora irá gerar um erro porque removaTodosObjetos não é declarado em qualquer interface público NSString que ele conhece.

Porque a classe de um objeto é determinado em tempo de execução, não faz nenhuma diferença que tipo você atribuir uma variável ao criar ou trabalhar com uma instância. Para usar as classes XYZPerssoa e XYZPessoaGritando descritos anteriormente neste capítulo, você pode usar o seguinte código:

XYZPessoa *primeiraPessoa = [[XYZPessoa alloc] init];

XYZPessoa *segundaPessoa = [[XYZPessoaGritando alloc] init];

[primeiraPessoa digaOla];

[segundaPessoa digaOla];

Embora ambos primeiraPessoa e segundaPerssoa são estaticamente tipados como objetos XYZPessoa, secondPerson irá apontar, no momento da execução, a um objeto XYZPessoaGritando. Quando o método digaOla é chamado em cada objeto, as implementações corretas serão usadas, para segundaPerssoa, isso significa a versão para XYZPessoaGritando.

Determinar a Igualdade de Objetos

Se você precisa determinar se um objeto é igual a outro objeto, é importante lembrar que você está trabalhando com ponteiros.

O operador de igualdade == C padrão é usado para testar a igualdade entre os valores de duas variáveis​​, como este:

if (algumInteiro == 42) {

//algumInteiro possue o valor 42

}

Ao lidar com os objetos, o operador == é usado para testar se dois ponteiros separados estão apontando para o mesmo objeto:

if (primeiraPessoa == segundaPessoa) {

//primeiraPessoa é o mesmo objeto como segundaPessoa

}

Se você precisa testar se dois objetos representam os mesmos dados, você precisa chamar um método como isEqual:, disponível a partir NSObject:

if ( [ primeiraPessoa isEqual: segundaPessa] ) {

//primeiraPessoa é identica a segundaPessoa

}

Se você precisa comparar se um objeto representa um valor maior ou menor do que outro objeto, você não pode usar os operadores de comparação padrão C> e <. Em vez disso, os tipos de fundamentos básicos, como NSNumber, NSString e NSDate, fornecem o método compare: como mostrado abaixo:

if ( [ algumaData compare:outraData] == NSOrderedAcending){

//someDate é anterior à anotherDate

}

Trabalhando Com Nill

É sempre uma boa idéia para inicializar variáveis ​​escalares no momento em que as declaramos, caso contrário, seus valores iniciais irão conter lixo a partir do conteúdo da pilha anteriores:

BOOL sucesso = NO;

int numeroMagico = 42;

Isso não é necessário para ponteiros de objeto, porque o compilador automaticamente vai definir a variável de zero se você não especificar qualquer outro valor inicial:
XYZPessoa *algumaPessoa;

// algumaPessoa é automaticamente configurada para nil.

Um valor nil é o caminho mais seguro para inicializar um ponteiro de objeto, se você não tem outro valor a ser usado, porque é perfeitamente aceitável em Objective-C para enviar uma mensagem a nil. Se você enviar uma mensagem para nil, obviamente, não acontece nada.

Se você precisa verificar para certificar-se de um objeto não seja nil (que uma variável aponta para um objeto na memória), você pode usar o operador de desigualdade padrão C:

if(algumaPessoa !=nil){

//algumaPessoa aponta para um objeto

}

ou simplesmente fornecer a variável:

if ( algumaPessoa) {

//algumaPessoa aponta para um objeto

}

Se a variável somePerson é nil, o seu valor lógico é 0 (falso). Se ele tem um endereço, e não for zero, então é avaliada como verdadeira.

Da mesma forma, se você precisa verificar se há uma variável nil, você pode usar o operador de igualdade:

if ( algumaPessoa == nil) {

//algumaPessoa não aponta para um objeto

}

ou simplesmente usar o operador de negação lógica C:

if ( !algumaPessoa) {

//algumaPessoa não aponta para um objeto

}

Exercícios

1 – Abra o arquivo main.m em seu projeto a partir dos exercícios no final do último capítulo e encontrar a função main (). Como acontece com qualquer executável escrito em C, esta função representa o ponto de partida para a sua aplicação.

Criar uma nova instância XYZPessoa usando alloc e init e, em seguida, chamar o método digaOla.

Nota: Se o compilador não solicita automaticamente, você precisará importar o arquivo de cabeçalho (contendo a interface XYZPessoa) no topo da main.m.

2 – Implementar o método digaAlgumaCoisa: mostrado no início deste capítulo, e reescreva o método digaOla para usá-lo. Adicione uma variedade de outras saudacoes e chamar cada um deles na instância que você criou anteriormente.

3 – Crie novos arquivos de classe para a classe XYZPessoaGritando, configure para herdar XYZPessoa.

Substituir o método digaAlgumaCoisa: para exibir a saudação maiúscula, e testar o comportamento em uma instância XYZPessoaGritando.

4 – Implemente o método de fábrica, da classe pessoa XYZPessoa que você declarou no capítulo anterior, para retornar uma instância corretamente alocada e inicializada da classe XYZPessoa, em seguida, usar o método main () em vez de seu alloc e init aninhado.

Dica: Ao invés de usar [[XYZPessoa alloc] init] no método de fábrica de classe, em vez de tentar usar [[self alloc] init].

Usando o self em um método de fábrica de classe significa que você está se referindo à própria classe.

Isso significa que você não tem que substituir o método pessoa na implementação XYZPessoaGritando para criar a instância correta. Teste isto verificando que:

XYZPessoaGritando *pessoaGritando = [XYZPessoaGritando pessoa];

Cria o tipo correto de objeto.

5 – Crie um novo ponteiro XYZPessoa local, mas que não inclua qualquer atribuição de valor.

Use um ramo (if) para verificar se a variável é automaticamente atribuída como nil.

 Meu email para contato: tiagofly@hotmail.com

Comentem sobre erros na tradução para corrigi-los. 

Obrigado a Todos!

Anúncios

4 comentários em “Programando com Objective-C Parte 1

  1. Thiago, gostei muito da sua iniciativa e quero lhe parabenizar pela mesma. Porém, vc deveria formatar o texto com uma fonte mais amena tipo Arial com tamanho 13px. O fundo escuro esta deixando a leitura muito cansativa, ainda mais com a cor cinza do texto. Os títulos dos tópicos não estão tendo o destaque necessário, isso não da a ideia que vc esta em um novo e/ou complementar assunto. Os códigos também estão sem destaque, se puder dar uma conselho, use plugins ou estilizações que simulem editores de código. Mas não quero só criticar… sua iniciativa é muito louvável e merece meu respeito. Se precisar de ajuda faça as traduções através do Github e compartilhe o link para podermos fazer um fork. No mais… Parabéns!

  2. Fala ae Leo!
    Realmente deixei muito a desejar nesta parte, pois foi mais por uma questão de tempo do que vontate. Eu tinha feito as apostilas pelo Google Drive, e deixava um link para visualização. Mas depois mudei e coloquei as apostilas diretamente no blog, o que fez aumentar o numero de páginas e conteúdo. Como você pode ver acabou ficando um material extenso demais para ser editado em pouco tempo. Sem falar que os recursos de edição no Blogger deixam a desejar, tanto que pensei em migrar o blog para outra site de blogs. Mas gostei sim da tua dica com relação ao Git, até pq eu poderíamos ter uma melhoria constantes das traduções.
    No momento estou focado em criar novo material e me aprofundar no conteúdo, sem falar no trabalho e casa filha e etc. Mas com certeza essas mudanças que vc citou deverão estar em primeiro lugar na pauta para futuras melhorias do blog. Mas espero que possam ajudar do mesmo modo.

    Agradeço de coração!

    Abraço e bons estudos!!

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s