Programando com Objective-C Parte 2

Aqui está a continuação da Tradução da Apostila: Programming With Objective-C da Apple. Lembrando que é uma tradução livre e pode conter erros de tradução. Portanto sempre na dúvida, comentar abaixo e juntamente pesquisar no documento original.

E para quem não viu a primeira parte é só clicar aqui: PARTE 1.

Obrigado

 

Encapsulamento de Dados

Além do comportamento das mensagens abrangidos no capítulo anterior, um objecto também encapsula dados através das suas propriedades.

Este capítulo descreve a sintaxe de Objective-C usada para declarar as propriedades de um objeto e explica como essas propriedades são implementadas por padrão através de síntese de métodos de acesso e variáveis ​​de instância. Se uma propriedade é mantida por uma variável de instância, essa variável deve ser definida corretamente em todos os métodos de inicialização.

Se um objeto precisa manter um vínculo com outro objeto através de uma propriedade, é importante considerar a natureza da relação entre os dois objetos. Embora o gerenciamento de memória para objetos Objective-C é principalmente feita para você através de contagem de referência automático (ARC), é importante saber como evitar problemas como fortes ciclos de referência, que levam a perdas de memória. Este capítulo explica o ciclo de vida de um objeto, e descreve a forma de pensar em termos de gestão de seu gráfico de objetos por meio de relacionamentos.

 

Propriedades Encapsulam os Valores de um Objeto

A maioria dos objetos precisam manter o controle da informação, a fim de executar suas tarefas. Alguns objetos são projetados para modelar um ou mais valores, como uma classe NSNumber Cocoa para manter um valor numérico ou uma classe XYZPessoa personalizado para modelar uma pessoa com primeiro e último nome. Alguns objetos são mais gerais no escopo, talvez lidar com a interação entre uma interface do usuário e as informações que ele exibe, mas mesmo esses objetos precisam manter o controle de elementos da interface do usuário ou o modelo de objetos relacionados.

Declarar Propriedades Públicas para os Dados Expostos

Propriedades Objective-C oferecem uma maneira de definir a informação que uma classe se destina a encapsular. Como você viu em “Propriedades de Controle de Acesso aos Valores de um Objeto”, declarações de propriedade estão incluídos na interface de uma classe, como esta:

@interface XYZPessoa: NSObject

@property NSString *primeiroNome;

@property NSString *ultimoNome;

@end

Neste exemplo, a classe XYZPessoa declara propriedades de string para manter o nome eo sobrenome de uma pessoa.

Tendo em conta que um dos princípios básicos em programação orientada a objeto é que um objeto deve esconder seu funcionamento interno por trás de sua interface pública, isto é importante pelo fato de acessar as propriedades de um objeto usando o comportamento exposto pelo objeto ao invés de tentar ter acesso aos valores internos diretamente .

Usando Métodos Acessores para Obter e Configurar (Get e Set) Valores de Propriedades

Você acessa ou define as propriedades de um objeto através de métodos de acesso:

NSString *primeiroNome = [algumaPessoa primeiroNome];

[algumaPessoa setPrimeiroNome:@”Jonas”];

Por padrão, esses métodos de acesso são sintetizados automaticamente para você pelo compilador, assim você não precisa fazer nada além de declarar a propriedade usando @property na interface de classe.

 

Os métodos sintetizados seguem as convenções de nomenclatura específica:

  • O método utilizado para acessar o valor (o método getter) tem o mesmo nome da propriedade.
  • O método getter para uma propriedade chamada primeiroNome também será chamado primeiroNome.
  • O método setter para uma propriedade chamada primeiroNome será chamado setPrimeiroNome:.

Se você não quer permitir que uma propriedade a seja alterada através de um método setter, você pode adicionar um atributo a uma declaração de propriedade para especificar que ela deve ser somente leitura:

@property (readonly) NSString *nomeCompleto;

Além de mostrar outros objetos como são supostos para interagir com a propriedade, atributos também dizem ao compilador como sintetizar os métodos de acesso relevantes.

Neste caso, o compilador irá sintetizar um método getter nomeCompleto, mas não um Método setNomeCompleto:.

Nota: O oposto de readonly (somente leitura) é readwrite (escrita e leitura). Não há necessidade de especificar o atributo readwrite explicitamente, porque ele é o padrão.

Se você quiser usar um nome diferente para um método de acesso, é possível especificar um nome personalizado, adicionando atributos da propriedade. No caso de propriedades booleanas (propriedades que têm um valor YES ou NO), é habitual para o método getter começar com a palavra “is”. O método getter para uma propriedade chamada terminar, por exemplo, deve ser chamado isTerminar.

Novamente, é possível adicionar um atributo na propriedade:

@property (getter=isTerminar) BOOL terminar;

Se você precisar especificar vários atributos, basta incluí-los como uma lista separada por vírgulas, como este:

@property (readonly, getter=isTerminar) BOOL terminar;

Neste caso, o compilador irá sintetizar apenas um método isTerminar, mas não um método setFinished:.

Nota: Em geral, os métodos de assesso à propriedade devem ser de valor-chave de codificação compatível (KVC), o que significa que eles seguem as convenções de nomenclatura explícitas.

Veja em Key-Value Coding Programming Guide para mais informações.

A Sintaxe de Ponto é uma Alternativa Concisa para Chamadas de Método de Assessor

Bem como tornar explícitos chamadas aos método de acesso, Objective-C oferece uma sintaxe de ponto alternativa para acessar as propriedades de um objeto.

A sintaxe de ponto permite que você acesse propriedades como neste exemplo:

NSString *primeiroNome = algumaPessoa.primeiroNome;

algumaPessoa.primeiroNome = @”Jonas”;

A sintaxe de ponto é puramente, um conveniente envólucro em torno das chamadas aos métodos de acesso. Quando você usar a sintaxe de ponto, as propriedades ainda serão acessadas ou mudadas usando os métodos de setter e getter mencionados acima.

Recebendo um valor usando algumaPessoa.primeiroNome é o mesmo que usar [algumaPessoa primeiroNome];

Definir um valor usando algumaPessoa.primeiroNome = @”Jonas” é o mesmo que usar [algumaPessoa setPrimeiroNome:@”Jonas”];

Isto significa que o acesso à propriedade através de sintaxe de ponto também é controlada pelos atributos de propriedades. Se uma propriedade é marcada somente leitura, você poderá obter um erro do compilador se você tentar defini-lo usando a sintaxe de ponto.

 

A Maioria das Propriedades são Suportadas por Variáveis ​​de Instância

Por padrão, uma propriedade readwrite serão suportadas por uma instancia de variavel, que novamente será sintetizada automaticamente pelo compilador.

Uma instancia de variável pe uma variável que e terá seu valor mantido pela vida do objeto. A memoria usada pelas instancias de variáveis serão alocadas quando o objeto é criado pela primeira vez (através de alloc), e liberadas quando o objeto é desalocado.

A menos que você especifique o contrário, a variável de instância sintetizada tem o mesmo nome da propriedade, mas com um prefixo sublinhado. Para uma propriedade chamada primeiroNome, por exemplo, a variável de instância sintetizada será chamado _primeiroNome:.

Embora seja a melhor prática para um objeto para acessar suas propriedades usando métodos de acesso ou ponto sintaxe de, é possível acessar a variável de instância diretamente de qualquer um dos métodos de instância numa implementação da classe:. O prefixo sublinhado deixa claro que você está acessando uma variável de instância, em vez de, por exemplo, uma variável local:

– (void) algumMetodo {

NSString *minhaString = @”Uma string interessante”;

_algumaString = minhaString;

}

Neste exemplo, é claro que minhaString é uma variável local e _algumaString é uma variável de instância:.

Em geral, você deve usar métodos de acesso ou sintaxe de ponto para acesso da propriedade, mesmo se você está acessando propriedades de um objeto dentro de sua própria implementação, caso em que você deve usar self:

– (void) algumMetodo{

NSString *minhaString = @”Uma string interessante”;

self.algumaString = minhaString;

// ou

[ self setAlgumaString: minhaString];

}

A exceção a esta regra é quando se escreve a inicialização, deallocation ou métodos de acesso personalizados, conforme descrito posteriormente nesta seção.

Você Pode Personalizar Os Nomes das Variáveis ​​da Instância Sintetizadas

Como mencinado anteriormente, o comportamento padrão para a escrever propriedades é o usar uma variável de instancia chamada _nomePropriedade.

Se voce quiser usar um nome diferente para a variável de instancia, você precisa direcinar o compilador para sintetizar a variável usando a seguinte sintaxe em sua implementação:

@implemetation SuaClasse

@synthesize nomePropriedade = nomeVariávelDeInstancia;

…//implementação

@end

Por exemplo:

@synthesize primeiroNome= ivar_primeiroNome;

Neste caso, a propriedade ainda será chamada primeiroNome, e ser acessível por meio de primeiroNome e métodos de acesso setPrimeiroNome: ou sintaxe de ponto, mas será apoiado(receberá) por uma variável de instância chamada ivar_firstName:.

Importante: Se você usar o @synthesize sem especificar um nome de variável de instância, como este:

@synthesize primeroNome;

a variável de instância terá o mesmo nome da propriedade.

Neste exemplo, a variável de exemplo, também será denominado primeiroNome, sem um sublinhado.

Você Pode Definir Variáveis de Instancia sem Usar Propriedades

É melhor prática para usar uma propriedade em um objeto a qualquer momento que você precisa manter o controle de um valor ou outro objeto.

Se você precisa definir suas próprias variáveis ​​de instância sem declarar uma propriedade, você pode adicioná-los entre chaves no topo da interface de classe ou de implementação, como esta:

AlgumaClasse.h

@interface AlgumaClasse : NSObject {

NSString *_minhaInstanciaDeVariávelNaoPropriedade;

}
… // Demais declarações

@end

**********

AlgumaClasse.m

@implementation AlgumaClasse {

NSString *_outraIstanciaDeVariavelCustomizada;

}

… // Implementação

@end

Nota: Você também pode adicionar variáveis ​​de instância no topo de uma extensão de classe, conforme descrito no “Class Extensions Extend the Internal Implementation.”

Acessar Instâncias de Variáveis Diretamente Por Métodos de Inicialização

Métodos setter podem ter efeitos colaterais adicionais. Eles podem desencadear notificações KVC, ou realizar outras tarefas se você escrever seus próprios métodos personalizados.

Você deve sempre acessar as variáveis ​​de instância diretamente de dentro de um método de inicialização porque no momento em que a propriedade é definida, o restante do objeto ainda não pode ser totalmente inicializado. Mesmo se você não fornecer métodos de acesso personalizados ou sabe de algum efeito colateral de dentro de sua própria classe, uma futura subclasse pode muito bem substituir o comportamento.

Um típico método init é assim:

– (id) init {
self = [super init];
if (self) {

//inicializa as instâncias de variáveis aqui

}
return self;
}

Um método init deve atribuir a self o resultado de chamar o método de inicialização da superclasse antes de fazer a sua própria inicialização. A superclasse pode falhar para inicializar o objeto corretamente e retornar nil, por isso você deve sempre verificar para ter certeza que self não seja nulo antes de realizar a sua própria inicialização. Ao chamar [super init] como a primeira linha do método, um objeto é inicializado a partir de sua classe raiz passando pelas implementações de init em cada subclasse na ordem de cima para baixo.

A figura abaixo mostra o processo para a inicializar um objeto XYZPessoaGritando:

Como você viu no capítulo anterior, um objeto é inicializado por cada chamada a init, ou chamanado um método que inicializa o objeto com valores específicos.

No caso da classe XYZPessoa, faria sentido oferecer um método de inicialização que defina inicialmente o primeiro e último nome da pessoa:

-(id) initWithFirstName:(NSString *)umPrimeiroNome ultimoNome:(NSString *)umUltimoNome;

Você deverá implementar o método como abaixo:

-(id)initWithFirstName: (NSString *)umPrimeiroNome ultimoNome:(NSString *)umUltimoNome {

self = [super init];

if (self) {
_primeiroNome = umPrimeiroNome;

_ultimoNome =umUltimoNome;

}
return self;
}

O Inicializador Designado é o Método de Inicialização Principal

Se um objeto declara um ou mais métodos de inicialização, você deve decidir qual método é o inicializador designado. Este é frequentemente o método que oferece mais opções para inicialização (tais como o método com a maioria dos argumentos), e é chamado por outros métodos que escrevem para a conveniência. Você também deve substituir geralmente init para chamar o inicializador designado com valores padrão adequados.

Se um XYZPessoa também tinha uma propriedade, a data de nascimento por exemplo, o inicializador designado pode ser:

-(id) initWithFirstName:(NSString *)umPrimeiroNome ultimoNome:(NSString *) umUltimoNome dataAniversario:(NSDate *)umDAN;

***OBS: DAN = Dia Aniversário.

Este método deve definir as variáveis ​​de instância relevantes, como mostrado acima. Se você ainda fornecer um inicializador conveniente para apenas o primeiro e último nome, você poderia implementar o método para chamar o inicializador designado, como este:
-(id) initWithFirstName: (NSString *) umPrimeiroNome ultimoNome: (NSString *)umUltimoNome {

returne [self initWithFirstName: umPrimeiroNome ultimoNome: umUltimoNome dataAniversario:nil];

}

Você também pode implementar um método padrão init para fornecer padrões adequados:

-(id) init {

return [self initWithFirsName:@”Jonas” ultimoNome:@”Silva” dataAniversario:nil];

}

Se você precisar escrever uma método de inicialização quando está subclassificando uma classe que usa multiplos métodos init, você deverá substituir o inicializador desiginado da superclasse para executar sua própria inicialização, ou adicionar sua propria inicialização adicional.

De qualquer maneira, você deve chamar inicializador designado da superclasse (no lugar de [super init];) antes de fazer qualquer de sua própria inicialização.

Você Pode Implementar Métodos de Acesso Personalizados

Propriedades nem sempre têm de ser “apoiados” por suas próprias variáveis ​​de instância. Como no exemplo, a classe XYZPessoa pode definir uma proprerty read-only (somente leitura) para o nome completo de uma pessoa:

@property (read-only) NSString *nomeCompleto;

Ao invés de ter que atualizar a propriedade nomeCompleto cada vez que o primeiro ou o último nome for alterado, seria mais fácil apenas escrever um método de acesso personalizado para construir a string com o nome completo no pedido:

-(NSString *) nomeCompleto {

return [NSString stringWithFormat:@”%@ %@”, self.primeiroNome, self.ultimoNome];

}

Este simples exemplo usa um formato string e especifica (como descrito no capítulo anterior) Por exemplo, é comum para atrasar a inicialização da property para construir uma string contendo o primeiro e último nome de uma pessoa separados por espaço.

Se você precisa escrever um método de acesso personalizado para uma propriedade que faz uso de uma variável de instância, você deve acessar a variável de instância diretamente de dentro do método.Por exemplo, é comum atrasar a inicialização de uma propriedade até que ela seja solicitada primeiramente, através de um “assessor ocioso”, como este:

-(XYZObjeto *) algumObjetoImportante {
if (!_algumObjetoImportante) {

_algumObjetoImportante = [[ XYZObjeto alloc] init];
}
return _algumObjetoImportante;
}

Antes de devolver o valor, este método primeiro verifica se a variável de instância _algumObjetoImportante é nulo, se ele for, ele aloca um objeto.

Nota: O compilador irá sintetizar automaticamente uma variável de instância em todas as situações onde é também síntesa pelo menos um método de acesso. Se você implementar um getter e um setter para a propriedade readwrite, ou um getter para a propriedade readonly, o compilador irá assumir que você está tomando o controle sobre a implementação da propriedade e não sintetizará uma variável de instância automaticamente. Se você ainda precisa de uma variável de instância, você vai precisar solicitar que um seja sintetizado:

@synthesize property = _property;

Propriedades são Atômicas por Padrão

Por padrão uma propriedade Objc-C é atomic:

@interface XYZObject : NSObject {

@property NSObject * implicitoObjetoAtomic //Atomico por padrão
@property (atomic) NSObject *explicitoObjetoAtomic // Explicitamente

// marcado como
// atomico.

@end

Isto significa que os assessores sintetizados garantem que um valor é sempre totalmente recuperada pelo método getter ou totalmente configurado através do método setter, mesmo se os assessores são chamados simultaneamente a partir de diferentes segmentos.

Devido a implementação e sincronização de métodos de acesso atomic serem privadas, não é possivel combinar um acessor sintetizado com um método de acesso que você mesmo implementou. Você receberá um aviso do compilador se você tentar, por exemplo, fornecer um setter personalizado para uma propriedade atomic,readwrite. Deixe o compilador sintetizar o getter.

Você pode usar o atributo de propriedade nonatomic para especificar que os assessores sintetizados simplesmente definem ou retornam um valor diretamente, sem garantias sobre o que acontece se esse mesmo valor é acessado simultaneamente de diferentes segmentos. Por esta razão, ele é mais rápido para acessar uma propriedade atômica do que um único atomic, e não há problema em combinar uma setter sintetizado, por exemplo, com sua própria implementação getter:

@interface XYZObjeto : NSObject

@property (nonatomic) NSObject *objetoNonatomic;

@end

********

@implementation XYZObjeto

-(NSObject *) objetoNonatomic {

return _objetoNonatomic;

}
// o setter irá ser sintetizado automaticamente

@end

Nota: Propriedade atomicidade não é sinônimo de thread-safe de um objeto.

Considere um objeto XYZPessoa em que ambos os nomes e sobrenomes de uma pessoa são alterados usando acessores atomic a partir de um segmento. Se outro thread(segmento) acessa os dois nomes ao mesmo tempo, os métodos getter atomic voltará com strings completas (sem travar), mas não há nenhuma garantia de que esses valores serão os nomes corretos em relação ao outro. Se o primeiro nome é acessada antes da mudança, mas o sobrenome é acessado após a mudança, você vai acabar com um par inconsistente, sem correspondência de nomes.

Este exemplo é bem simples, mas o problema de segurança do thread torna-se muito mais complexa quando considerado através de uma rede de objetos relacionados. Thread safety é abordado com mais detalhes no Guia de Programação Concorrente.

Gerenciar o Gráfico do Objeto Através da Propriedade e Responsabilidade

Como você já viu, memória para Objective-C objetos é alocada dinamicamente (na pilha), o que significa que você precisa usar ponteiros para acompanhar o endereço de um objeto. Ao contrário dos valores escalares, nem sempre é possível determinar um tempo de vida de um objeto do escopo de uma variável ponteiro. Em vez disso, um objeto deve ser mantido ativo na memória por tanto tempo quanto for necessário por outros objetos.

Ao invés de tentar se preocupar em gerenciar o ciclo de vida de cada objeto manualmente, você deveria pensar sobre as relações entre os objetos.

No caso de um objeto XYZPessoa, por exemplo, as duas propriedades string de nome e sobrenome são efetivamente “propriedade” da instância XYZPessoa. Isto significa que eles devem permanecer na memória, enquanto o objecto XYZPessoa permanece na memória.

Quando um objeto depende de outros objetos, desta forma, efetivamente tomam posse desses outros objetos, o primeiro objeto é dito ter referência strong aos outros objetos. Em Objective-C, um objeto é mantido vivo desde que tenha pelo menos uma referência strong para ele a partir de outro objeto. As relações entre a instância XYZPessoa e os dois objetos NSString é mostrado abaixo:

Quando um objeto XYZPessoa é desalocado da memória, os dois objetos string também serão desalocados, assumindo que não existem quaisquer outras referências fortes que lhes restam.

Para acrescentar um pouco mais de complexidade para este exemplo, considere o gráfico do objeto de um aplicativo como o mostrado abaixo:

Quando o usuário clica no botão Atualizar, a visualização crachá é atualizado com as informações do nome relevante.

A primeira vez detalhes de uma pessoa são introduzidas eo botão de atualização clicado, o gráfico de objeto simplificado pode parecer:

Quando o usuário modifica o nome da pessoa, gráfico de objeto é modificado como visto a seguir:

A visualização do crachá mantém uma forte relação com o original objeto string @”John”, mesmo que o objeto XYZPessoa agora possui primeiroNome diferente. Isto significa que o objeto @ “John” permanece na memória, usado pelo vista crachá para imprimir o nome.

Uma vez que o usuário clica no botão Atualizar uma segunda vez, a visão do crachá é solicitada a atualizar suas propriedades internas para corresponder ao objeto pessoa, de modo que o gráfico do objeto parece:

Neste ponto o objeto original @”John” já não tem quaisquer referência forte, por isso ele é removido da memória.

Por padrão, ambas as propriedades e as variáveis ​​de Objective-C mantêm fortes referências aos seus objetos. Isso é bom para muitas situações, mas não causa um problema potencial com fortes de ciclos de referência.

Anulando Ciclos de Referência Fortes

Embora as referências fortes funcionam bem para as relações de sentido entre os objetos, é preciso ter cuidado quando se trabalha com grupos de objetos interligados. Se um grupo de objetos está ligado por um círculo de relacionamentos fortes, eles manteram o outro vivo, mesmo se não houver fortes referências de fora deste grupo.

Um exemplo óbvio de um potencial ciclo de referência quando existe entre dois objetos um objeto table view (UITableView para IOS e NSTableView para OS X), e seu delegate.

Para que uma classe table view seja útil em várias situações, ele delega em algumas decisões a objetos externos. Isto significa que ele depende de outro objeto para decidir qual conteúdo que irá exibir, ou fazer caso o usuário interagir com uma específica entrada de comando na table view.

Um cenário comum é que a table view tem uma referência para o seu delegate, e o delegate tem uma referência de volta para a table view, como mostrado abaixo:

Um problema ocorre se os outros objetos desistirem de suas fortes relações com a table view e delegate, como mostrado abaixo

Mesmo que não há nenhuma necessidade dos objetos sejam mantidos na memória – não há fortes relações com a table view ou delegate ou outras relações entre os dois os objetos – as duas fortes relações restantes mantém os dois objetos vivos. Isto é conhecido como um forte ciclo de referência.

A maneira de resolver este problema é substituir uma das fortes referências de uma referência fraca. Uma referência fraca não implica a propriedade ou responsabilidade entre dois objetos, e não mantém um objeto vivo.

Se a table view é modificada para usar uma relação fraca com seu delegate (que é como UITableView e NSTableView resolvem este problema) o gráfico do objeto inicial agora se parece com isso:

Quando os outros objetos no gráfico desistirem de suas fortes relações com a table view e delegate neste momento, não há referências fortes deixadas ao objeto delegado, como mostrado abaixo:

Isto significa que o objeto delegado vai ser desalocado, libertando assim a forte referência na table view, como mostrado abaixo:

Uma vez que o delegate é desalocado, não há mais quaisquer referências fortes para a table view, por isso também é liberada.

Use Declarações Fortes e Fracas para Gerenciar a Propriedade

Por padrão, as propriedades dos objetos são declarados deste modo:
@property id delegate;

usar referências fortes para suas variáveis ​​de instância sintetizadas. Para declarar uma referência fraca, adicione um atributo na propriedade, como neste exemplo:

@property (weak) id delegate;

Nota: O opositor de weak(Fraco) é strong (Forte). Não há necessidade de especificar o atributo strong explicitamente, porque ele é o padrão.

As variáveis locais ( e não propriedades de variáveis de instancia) também mantêm fortes referências a objetos por padrão. Isso significa que o seguinte código irá funcionar exatamente como você espera:

NSDate *dataOriginal = self.lastModificationDate;

self.lastModificationDate = [NSDate date];

NSLog(@” Ultima modificação de data mudada de %@ para %@”, dataOriginal, self.lastModificationDate);

Neste exemplo, a variável local dataOriginal mantém uma forte referencia com o objeto inicial lastModificationDate. Quando a propriedade lastModificationDate é alterada, a propriedade já não mantém uma referencia forte para a data original, mas essa data ainda é mantida a salvo pela variável forte dataOriginal.

Nota: A variável mantém uma forte referencia para um objeto somente enquanto que a variável está no escopo, ou até que seja transferido para outro objeto ou nulo.

Se você não quer que uma variável para manter uma referência forte, você pode declará-la como __ weak, como neste exemplo:

NSObject *_weak variavelFraca;

Portanto uma referência fraca não manterá um objeto vivo, isto possibilita ao objeto referenciado ser desalocado enquanto a referência ainda esá em uso. Para evitar um perigoso dangling pointer na memória originalmente ocupada pelo objeto agora desalocado, uma referencia fraca é automaticamente definida como nil, quando o seu objeto é desalocado.

Isto siginifica que se voce usar uma varável weak no exemplo de date mostrado anteriormente ficará assim:

NSDate *_weak dataOriginal = self.lastModificationDate;

self.lastModificationDate = (NSDate date);

a variável dataOriginal poderá potencialmente ser configurada para nil. Quando self.lastModificationDate é reatribuído, a propriedade não manterá uma referencia forte com a original data. Se não há outras referências fortes para ele, a data original será desalocado e originalDate será configurada para nil.

Variáveis fracas podem ser um ponto de fusão, particularmente como no seguinte exemplo:

NSObject *_weak algumObjeto = [[NSObject alloc] init];

Neste exemplo, o recêm objeto alocado não terá uma referência forte para ele, por isso é imediatamente desalocado e algumObjeto é definido como nil.

Também é importante considerar as implicações de um método que precisa acessar uma propriedade fraca várias vezes, como neste exemplo:

-(void) algumMetodo {
[self.weakProperty facaAlgo];

[self.weakProperty facaAlgoTambém];

}

Em situações como essa, você pode querer armazenar cache aa propriedade fraca em uma variável forte para garantir que ele seja mantido na memória, desde que você precise usá-la:

-(void) algumMetodo {

NSObject *cashObject = self.weakProperty;

[cachedObject facaAlgo];

[cachedObject facaAlgoTambem];

}

Neste exemplo, a variável cachedObject mantém uma forte referência ao valor original da propriedade fraca de modo a que ele não pode ser desalocada enquanto cachedObject ainda está no âmbito de aplicação (e não foi transferido um outro valor).

É particularmente importante manter isso em mente, se você precisa ter certeza de uma propriedade fraco não é nulo antes de usá-lo. Não é suficiente apenas para testá-lo, como neste exemplo:

if (self.someWeakProperty) {

[someObject doSomethingImportantWith: self.someWeakProperty];

}

porque em uma aplicação multi-threaded, a propriedade pode ser desalocado entre o teste ea chamada de método, tornando o teste inútil. Em vez disso, você precisa declarar uma variável local forte para armazenar em cache o valor, assim:

NSObject *cachedObject = self.someWeakProperty; //1

if(cachedObject){ //2

[someObject doSomethingImportantWith: cachedObject]; //3

} //4
cachedObject = nil; //5

Neste exemplo, a referencia forte é criada na linha 1, significando que o objeto está garantido a estar vivo para teste(linha 2) e chamada do método(linha 3). Na linha 5, cachedObject é configurado como nil, dando assim a referência forte. Se o objeto original não possuir outra referencia forte(strong) neste ponto, será desalocado e someWeakProperty será configurado como nil.

Usar Referências Inseguras não Retidas para Algumas Classes

Estas são umas poucas classes em Cocoa e Cocoa Touch que não suportam referências fracas, o que siginifica que você não poderá declarar uma propriedade como weak ou variável local como weak para manter o controle delas. Estas classes incluem NSTextView, NSFont e NSColorSpace; para ver a lista completa, veja aqui: Transitioning to ARC Release Notes.

Caso você precise usar uma referencia fraca para uma dessas classes, você deve usar uma referência insegura. Para uma propriedade, isto siginifica usar o atributo unsafe_unretained:

@property (unsafe_unretained) NSObject *unsafeProperty;

Para variáveis, você precisará usar _unsafe_unretained:

NSObject *__unsafe_unretained unsafeReference;

Uma referência insegura é similar a uma referencia weak (fraca), na medida em que não mantém seu objeto relacionado vivo, mas não vai ser definido como nil se o objeto de destino está desalocado. Isso significa que você vai ficar com um ponteiro pendente na memória originalmente ocupado pelo objeto agora desalocado, daí o termo “inseguro”. Envio de uma mensagem para um ponteiro pendente irá resultar em um acidente.

Copiar Propriedades Mantêm suas Próprias Cópias

Em algumas circunstâncias, um objeto pode querer manter sua própria cópia de quaisquer objetos que são definidos por suas propriedades.

Como um exemplo, a interface de classe a classe XYZBadgeView mostrado anteriormente…

pode ter esta aparência:

@interface XYZBadgeView : NSView

@property NSSTring *primeiroNome;
@property NSString *ultimoNome;

@end

Duas propriedades NSString são declarados, ambos mantêm fortes referências implícitas aos seus objetos.

Considere o que acontece quando um outro objeto cria uma seqüência a ser definida como uma das propriedades da badge view, como esta:

NSMutableString *stringNome = [NSMutableString stringWithString:@”Jonas”];

self.badgeView.primeiroNome = stringName;

Isto é perfeitamente válido, porque NSMutableString é uma subclasse de NSString. Embora o ponto de badge view pensa que está lidando com uma instância NSString, ele está realmente lidando com um NSMutableString.

Isto significa que a string pode mudar:

[stringNome appendString:@”ny”];

Neste caso, embora o nome “João” no momento em que foi originalmente criado para a propriedade firstName da badge view, agora é “Jonas”, porque a string mutável foi alterado.

Você poderá escolher oque badge view deverá manter suas próprias cópias de todas as strings definidas parar as suas propriedades primeiroNome e ultimoNome, para que ele efetivamente capture as strings no momento em que as propriedades são definidas.Através da adição de um atributo de cópia para as duas declarações de propriedade:

@XYZBadgeView : NSView

@property (copy) NSString *primeiroNome;

@property (copy) NSString *ultimoNome;

@end

a view agora mantem suas proprias cópias das duas strings. Entretanto se uma mutable string for configurada e subsequentemente alterada, a badge view irá capturar o valor que ele tem no momento em que é definido. Por exemplo:

NSMutableString *stringNome = [NSMutableString stringWithString:@”João”];

self.badgeView.primeiroNome = nomeString;

[nomeString appendString:@”ny”];

Desta vez, o primeiroNome, mantida pela badge view será uma cópia não afetada da string original “João”.

O atributo copy siginifica que a propriedade usa uma referência forte, portanto, isso dever ser mantido no novo objeto criado.

Nota: Qualquer objeto que você deseja configurar como uma propriedade copy deve ser suportado por NSCopying, o que siginifca que deve estar de acordo com o protocolo NSCopying.

Protocolos estão descritos em “Os Potocolos Definem os Contratos de Mensagens.” Para mais informações de NSCopying, veja NSCopying ou Guia de Programação de Gerenciamento de Memória Avançada.

Se você precisa definir a variável de instância de uma propriedade cópia diretamente, por exemplo, um método inicializador, não se esqueça de definir uma cópia do objeto original:

-(id) initWithSomeOriginalString: (NSString *) umaString {

self = [ super init];

if (self) {

__instanceVariableForCopyProperty = [umaString copy];

}
return self;

}

Exercícios

1 – Modifique o método digaOla da classe XYZPessoa para registrar uma saudação usando o primeiro e ultimo nome.

2 – Declarar e implementar um novo inicializador designado usado para criar uma XYZPessoa usando o primeiro nome especificado, sobrenome e data de nascimento, juntamente com uma adequada classe método de fábrica.

Não se esqueça de override(sobreescrever) init para chamar o inicializador designado.

3 – Teste o que acontece se você definir uma string mutável como o primeiro nome da pessoa, então transformar essa string antes de chamar o método digaOla modificado. Altere as declarações de propriedade NSString adicionando o atributo cópia e teste novamente.

4 – Tente criar objetos XYZPessoa usando uma variação de variáveis Fortes e Fracas na função main(). Verifique se as variáveis ​​fortes mantem os objetos XYZPessoa vivos, pelo menos enquanto você espera.

A fim de ajudar a verificar quando um objeto XYZPessoa é desalocado, você pode querer amarrar em ciclo de vida do objeto, fornecendo um método dealloc na implementação XYZPessoa. Este método é chamado automaticamente quando um objeto Objective-C é desalocado da memória, e é normalmente usado para liberar qualquer memória que você alocou manualmente, como através da função C malloc (), conforme descrito no Guia de Programação Gerenciamento Avançado de Memória.

Para os fins deste exercício, substitua o método dealloc em XYZPessoa para registrar uma mensagem, assim:

(void) dealloc {

NSLog(@”XYZPessoa está sendo desalocado”);

}

Tente configurar cada variável ponteiro XYZPessoa a zero para verificar se os objetos estão liberados quando você espera que eles sejam.

Nota: O modelo de projeto do Xcode para uma ferramenta de Linha de Comando usa um bloco @ autoreleasepool {} dentro da função main (). Para utilizar o recurso Automatic Retain Cout do compilador para lidar com gerenciamento de memória para você, é importante que qualquer código que você escrever em main () fique dentro deste bloco autorelease pool.

Autorelease Pools estão fora do escopo deste documento, mas são abordados em detalhes no Guia de Programação gerenciamento avançado de memória.

Quando você está escrevendo uma aplicação Cocoa Touch ou Cocoa ao invés de uma ferramenta de linha de comando, você geralmente não precisa se preocupar com a criação de suas próprias autorelease pools porque você está amarrando em uma framework de objetos que irão garantir um já está no local.

5 – Modifique a classe XYZPessoa Modifique descrição da classe XYZPerssoa para que você possa acompanhar o cônjuge ou parceiro.

Você precisa decidir qual a melhor forma para modelar o relacionamento, pensar cuidadosamente sobre o gerenciamento gráfico de objeto.

 

Customizando Classes Existentes

Os objetos devem ter tarefas claramente definidas, como a modelagem de informações específicas, a exibição de conteúdo visual ou controlar o fluxo de informações. Como você já viu, uma interface de classe define as formas em que os outros são esperados para interagir com um objeto para ajudar a realizar essas tarefas.

Algumas vezes, você poderá encontra oque deseja ao extender uma classe existente pela adição de comportamenteo que é util somente em certas ocasiões.

Como exemplo, você pode achar que a sua aplicação, muitas vezes precisa exibir uma seqüência de caracteres em uma interface visual. Ao invés de criar um objeto string de desenho para usar todas as vezes que você precisa para exibir uma seqüência, não faria mais sentido se fosse possível dar a classe NSString em si a capacidade de desenhar seus próprios caráteres na tela.

Em situações como esta, que nem sempre faz sentido adicionar o comportamento de utilidade para a interface de classe original primário. Habilidades de desenho são improváveis de serem necessário na maioria das vezes, qualquer objeto string é usado em um aplicativo, por exemplo. E no caso de NSString, você não pode modificar a interface original ou implementação porque é uma classe do framework.

Além disso, ele pode não fazer sentido para subclasse da classe existente, porque você pode querer o seu comportamento de desenho disponível não só para a classe NSString original, mas também quaisquer subclasses dessa classe, como NSMutableString. E, apesar de NSString está disponível em ambos OS X e iOS, o código de desenho precisa ser diferente para cada plataforma, por isso você precisa usar uma subclasse diferente em cada plataforma.

Em vez disso, Objective-C permite que você adicione seus próprios métodos para classes existentes através de categorias e extensões de classe.

 

Categorias Adicionam Métodos a Classes Existentes

Se você precisa adicionar um método a uma classe existente, para adicionar uma funcionalidade para torná-lo mais fácil de fazer algo, em seu próprio aplicativo, a maneira mais fácil é usar uma Categoria.

A sintaxe para declarar uma categoria usa a palavra-chave @interface, assim como uma descrição padrão de classe Objective-C, mas não indica qualquer herança de uma subclasse. Em vez disso, ele especifica o nome da categoria entre parênteses, como no exemplo:

@interface ClassName (CategoryName)

@end

Uma categoria pode ser declarada para qualquer classe, mesmo se você não possui o código fonte da implementação original ( tal como para as classes padrão de Coco ou Cocoa Touch). Qualquer método que você declara em uma categoria estará disponível para todas as instancias da classe original, assim como qualquer subclasse da classe original. Em tempo de execução (runtime), não há nenhuma diferença entre um método adicionado por uma categoria e outra que é implementado pela classe original.

Considere a classe XYZPessoa do capítulo anterior, que tem propriedades para o primeiro e ultimo nome de um objeto pessoa. Se você está escrevendo um aplicativo de Gravação-Registro, você vai descobrir que você freqüentemente precisa exibir uma lista de pessoas pelo sobrenome, como no exemplo abaixo:

Appleseed, John

Doe, Jane

Smith, Bob

Warwick, Kate

Ao invés de ter que escrever um código para gerar um string de ultimoNome, primeiroNome adequado a cada vez que você queira exibir, você pode adicionar uma categoria para a classe XYZPessoa, como no exemplo:

#import “XYZPessoa.h”

@interface XYZPessoa (XYZPessoaAddExibirNomeDePessoa)

-(NSString *) ultimoNomePrimeiroNomeString;

@end

Neste exemplo, a categoria (XYZPessoaAddExibirNomeDePessoa) declara um método adicional para retornar uma string nescessária.

Uma categoria é normalmente declarada em um arquivo de cabeçalho separado e implementado em um arquivo de código fonte em separado. No caso de XYZPessoa, você pode declarar a categoria em um arquivo de cabeçalho chamado XYZPessoa+XYZPessoaAddExibirNomeDePessoa.

Apesar de todos os métodos adicionados por uma categoria estão disponíveis para todas as instâncias da classe e suas subclasses, você precisa importar o arquivo de cabeçalho da categoria em qualquer arquivo de código fonte onde você deseja usar os métodos adicionais, caso contrário, você vai executar com avisos do compilador e erros.

A implementação da categoria pode ter esta aparência:

#import “XYZPessoa+XYZPessoaAddExibirNomeDePessoa.h”

@implemetation AlgumObjeto

-(void) algumObjeto {

XYZPessoa *pessoa = [[XYZPessoa alloc] initiWithFirstName:@”Joao” lastName:@”Doe”];

XYZPessoaGritando *pessoaGritando = [[XYZPessoaGritando alloc] initWithFirstName:@”Monica” lastName@”Robinson”];

NSLog(@”As duas Pessoas são %@ e %@, [pessoa lastNameFirstNameString], [pessoaGrintando lastNameFirstNameString]);

}

@end

Bem como apenas adicionar métodos para classes existentes, você também pode usar as categorias para dividir a implementação de uma classe complexa entre vários arquivos de código fonte. Você pode, por exemplo, colocar o código de desenho, de um elemento de interface do usuário personalizada, em um arquivo separado para o resto da execução, se os cálculos geométricos, cores e gradientes, etc, são particularmente complicados. Alternativamente, você pode oferecer diferentes implementações para os métodos de categoria, dependendo se você estivesse escrevendo um aplicativo para OS X e iOS.

Categorias podem ser usadas para declarar tanto os métodos de instância ou métodos de classe, mas geralmente não são adequados para declarar propriedades adicionais. Esta é uma sinxtaxe válida para incluir uma declaração de propriedade em uma interface de categoria, mas não é possível declarar uma variável de instância adicional em uma categoria. Isto significa que o compilador não vai sintetizar qualquer variável de instância, nem vai sintetizar os métodos de assesso de propriedade. Você pode escrever seus próprios métodos de acesso na implementação da categoria, mas você não será capaz de acompanhar o valor dessa propriedade, a menos que ele já está armazenado pela classe de origem.

A única maneira de adicionar uma propriedade tradicional – apoiado por uma nova variável de instância – a uma classe existente é a utilização de uma extensão de classe, conforme descrito em Extensões de Classe e Estender a Implementação Interna.

Nota: Cocoa e Cocoa Touch incluem uma variedade de categorias para algumas das principais classes do framework.

A funcionalidade desenhar-string mencionada na introdução deste capitulo é de fato providenciada para NSString através da categoria NSStringDrawing para OS X, que incluem os métodos drawAtPoint:withAttributes: e drawInRect:withAttributes: . Para IOS a categoria UIStringDrawing inclui métodos tais como drawAtPoint:withFont: e drawInRect:withFont:.

Evite Conflito de Nome dos Métodos de Categorias

Portanto os métodos declarados em uma categoria são adicionados para uma classe existente, por iss você precisa ser muito cuidadoso com o nome dos métodos.

Se o nome de um método declarado em uma categoria é o mesmo que um método na classe original, ou um método em outra categoria na mesma mesma classe ( ou mesmo uma superclasse), o comportamento é indefinido quando à implementação do método, que é usado em tempo de execução. Isso é menos provável de ser um problema se você estiver usando categorias com suas próprias classes, mas pode causar problemas ao usar categorias para adicionar métodos a classes padrões em Cocoa ou Cocoa Touch.

Um aplicativo que funciona com um serviço de web remoto, por exemplo, pode precisar de uma maneira fácil de codificar uma seqüência de caracteres usando a codificação Base64. Faria sentido definir uma categoria em NSString para adicionar um método de instância para retornar uma versão em Base64 de uma string, então você pode adicionar um método conveniência chamado base64EncodedString.

Um problema surge se você conectar um outro framework que também passa a definir sua própria categoria em NSString, incluindo o seu próprio método chamado base64EncodedString. Em tempo de execução, apenas uma das implementações do método vai “ganhar” e será adicionado ao NSString, mas qual deles será usado é indefinido.

Outro problema pode surgir se você adicionar métodos convenientes para as classes Cocoa Touch ou Cocoa, que depois são adicionados às classes originais em versões posteriores. A classe NSSortDescriptor, por exemplo, que descreve como uma coleção de objetos devem ser ordenados, sempre teve um initWithKey: ascending: método de inicialização, mas não oferecem um método de fábrica classe correspondente no começo das versões OS X e do iOS.
 

Por convenção, o método de fábrica de classe deve ser chamado sortDescriptorWithKey: ascending:, de modo que você possa escolher adicionar uma categoria no NSSortDescriptor para fornecer este método. Isso teria funcionado como esperado em versões mais antigas do OS X e iOS, mas com o lançamento do Mac OS X versão 10.6 e iOS 4.0, um método sortDescriptorWithKey: ascending: foi adicionado à classe original NSSortDescriptor, ou seja, siginificando que você terminaria agora com um conflito de nomes quando sua aplicação fosse executada nestas versões ou em versões posteriores.

A fim de evitar um comportamento indefinido, é a melhor prática adicionar um prefixo para nomes de métodos em categorias de classes do framework, assim como você deve adicionar um prefixo para os nomes de suas próprias classes. Você pode optar por usar as mesmas três letras que você usa para seus prefixos de classe, mas minúsculas para seguir a convenção usual para nomes de métodos, em seguida, um sublinhado, antes do resto do nome do método. Para o exemplo NSSortDescriptor, sua própria categoria pode ter esta aparência:

@interface NSSortDescriptor (XYZAdditions)

-(id) xyz_sortDescriptionWithKey>(NSString *(key) ascending:(BOOL) ascending;

@end

Isto siginifica que você pode ter certesa que ser método será usando em tempo de execução. A ambiguidade é removida devido ao seu código, como neste exemplo:

NSSortDescriptor *descriptor = [NSSortDescriptor xyz_sortDescriptorWithKey:@”name” ascending:YES];

 

Extensões de Classe e Estender a Implementação Interna

A extensão de classe tem algumas semelhanças com a categoria, mas somente pode ser adicionado a uma classe para a qual você tem o código-fonte em tempo de compilação ( a classe é compilada ao mesmo tempo que a extensão da classe). Os métodos declarados por uma extensão de classe são implementados no bloco @implementation da classe original, de modo que você não possa, por exemplo, declarar uma extensão de classe em uma classe do framework, como classes de Cocoa e Cocoa Touch, como NSString.

A sintaxe para declarar uma extensão de classe é parecida como a sintaxe para uma categoria como no exemplo:

@interface Classname()

@end

Como nenhum nome é dado entre parênteses, as extensões de classe são muitas vezes referidos como categorias anônimos.

Ao contrário de categorias normais, uma extensão de classe pode adicionar suas próprias propriedades e variáveis ​​de instância de uma classe. Se você declarar uma propriedade em uma extensão de classe, assim:

@interface XYZPessoa()

@property NSObject *extraProperty;

@end

o compilador irá sintetizar automaticamente os métodos de acesso aplicáveis, bem como uma variável de instância, dentro da implementação da classe primária.

Se você adicionar todos os métodos em uma extensão de classe, estes devem ser implementadas na implementação primária para a classe.

Também é possível usar uma extensão de classe para adicionar variáveis ​​de instância personalizados. Estes são declarados entre chaves na interface da extensão de classe:

@interface XYZ() {

id _someCustomInstanceVariable;

}

@end

Use Extensões de Classes para Ocultar Informações Privadas

A interface primária para uma classe é usado para definir o modo como as outras classes deverão interagir com ele. Em outras palavras, é a interface pública para a classe.

Extensões de classe são muitas vezes utilizados para estender a interface pública com métodos privados adicionais ou propriedades para uso dentro da implementação da própria classe. É comum, por exemplo, para definir uma propriedade como somente leitura na interface, mas como readwrite em uma extensão de classe declarada antes da implementação, a fim de que os métodos internos da classe possam alterar o valor da propriedade diretamente.

Como exemplo, a classe XYZPerssoa pode adicionar uma propriedade chamada uniqueIdentifier, projetado para manter o controle de informações como um número de Segurança Social nos EUA.

Isso geralmente requer uma grande quantidade de papelada para ter um identificador único atribuído a um indivíduo no mundo real, por isso a interface de classe XYZPerssoa pode declarar essa propriedade como somente leitura e proporcionar algum método que solicita que um identificador seja atribuído, como este:

@inteface XYZPessoa : NSObject

@property (readonly) NSString *uniqueIdentifier;

-(void)assignUniqueIdentifier;

@end

Isto significa que não é possível ao uniqueIdentifier ser definido diretamente por outro objeto. Se uma pessoa não tiver uma identificação, a solicitação deve ser feita, para atribuir um identificador, chamando o método assignUniqueIdentifier.

Para que a classe XYZPerssoa seja capaz de alterar a propriedade internamente, faz sentido declarar novamente a propriedade em uma extensão de classe que é definido no início do arquivo de implementação para a classe:

@interface XYZPessoa ()

@property (readwrite) NSString *uniqueIdentifier;

@end

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

@implementention XYZPessoa

@end

Nota: O atributo readwrite é opcional, porque é o padrão. Nota: O atributo readwrite é opcional, porque é o padrão. Você pode gostar de usá-lo quando redeclarar uma propriedade, para maior clareza.

Isso significa que o compilador agora também sintetizará um método setter, portanto, qualquer método dentro da implementação XYZPerssoa será capaz de definir a propriedade diretamente usando o setter ou sintaxe de ponto.

Ao declarar a extensão de classe dentro do arquivo de código fonte para a implementação XYZPessoa, a informação permanece privada para a classe XYZPessoa. Se outro tipo de objeto tentar definir a propriedade, o compilador irá gerar um erro.

Nota: Ao adicionar a extensão de classe mostrado acima, redeclarar a propriedade uniqueIdentifier como uma propriedade readwrite, um método setUniqueIdentifier: vai existir em tempo de execução em cada objeto XYZPessoa, independentemente de outros arquivos de código fonte estavam cientes da extensão de classe ou não.

O compilador vai reclamar se o código em um desses outros arquivos de código fonte tentar chamar um método privado ou definir uma propriedade somente leitura, mas é possível evitar erros do compilador e aproveitar as características de execução dinâmicas para chamar esses métodos de outras maneiras, como por meio de um dos performSelector: … são métodos oferecidos por NSObject. Você deve evitar uma hierarquia de classes ou de design, onde isto é necessário, em vez disso, a interface de classe principal deve sempre definir as interações “públicos” corretas .

Se você pretende fazer métodos “privados” ou propriedades disponíveis para selecionar outras classes, como as classes relacionadas dentro de uma estrutura, você pode declarar a extensão de classe em um arquivo de cabeçalho separado e importá-lo nos arquivos de origem que precisam dele. Não é incomum ter dois arquivos de cabeçalho para a classe, por exemplo, como XYZPerson.h e XYZPersonPrivate.h. Quando você atualizar o framework, você só atualizará o arquivo de cabeçalho XYZPerson.h público.

Considerando Outras Alternativas para Customizar Classes

Categorias e extensões de classes tornam facil adiconar comportamento diretamente em uma classe existente, mas as vezes isso não é a melhor opção.

Um dos principais objetivos da programação orientada a objeto é escrever código reutilizável, o que significa que as classes devem ser reutilizáveis ​​em uma variedade de situações, sempre que possível. Se você estiver criando uma classe view para descrever um objeto que exibe informações na tela, por exemplo, é uma boa idéia pensar se a classe pode ser utilizável em várias situações.

Ao invés de criar um código com dificuldades, decisões sobre o layout e conteúdo, uma alternativa é a alavancação por herança e deixar estas decisões para métodos espevificamente projetado para sobescrito por subclasses. Embora isso faça com que seja relativamente fácil de reutilizar a classe, você ainda precisa criar uma nova subclasse cada vez que você quiser fazer uso dessa classe de origem.

Outra alternativa é uma classe usar um objeto delegate. Qualquer decisão que possa ser limitada a reutilização pode ser delegada a um outro objeto, que é deixado para tomar essas decisões em tempo de execução. Um exemplo comum é uma classe de exibição de tablea padão (NSTableView para OS X e UITableView para IOS). Para que uma exibição de tabela genérica(um objeto que exibe informações através de uma ou mais colunas e linhas) para ser útil

Outra alternativa é uma classe usar um objeto delegate. Qualquer decisão que possam limitar a reutilização pode ser delegado para outro objeto, que é responsãvel por esta decisão em tempo de execução. Um exemplo comum é uma tabela padrão(NSTableView em OS X e UITableView no IOS). Para que uma visão de tabela (table view) (um objeto que mostra informações uma ou mais linhas ou colunas) possa ser útil, deixe as decisões sobre o seu conteúdo a ser decidido por outro obejeto em tempo de execução.Delegação é abordada em detalhes no próximo capítulo “Trabalhando com Protocolos”.

Interagir Diretamente com o Objective-C em Tempo de Execução

Objective-C oferece seu comportamento dinâmico através do sistema de execução Objective-C.
Muitas decisões, tais como quais os métodos são chamados quando as mensagens são enviadas, não são feitas em tempo de compilação, mas ao invés disso são determinadas quando o aplicativo é executado. Objective-C é mais do que apenas uma linguagem que é compilada até o código de máquina. Em vez disso, ele requer um sistema de execução no local para executar esse código.

É possível interagir diretamente com o sistema de tempo de execução, como pela adição de referências associativas para um objeto. Ao contrário de extensões de classe, referências associadas não afetam a declaração da classe original e implementação, o que significa que você pode usá-los com as classes do framework para as quais você não tem acesso ao código-fonte original.

Uma referência associativa conecta um objeto com outro, de uma maneira parecida com uma propriedade com uma variável de instancia. Para mais informações, veja “Referencias Associativas”. Para saber mais sobre Objective-C Runtime, veja: “Guia de programação Objective-C Runtime”.

Exercícios:

1 – Adicione uma categoria para a classe XYZPessoa declarar e implementar o comportamento adicional, como exibir o nome de uma pessoa de maneiras diferentes.

2 – Adicione uma categoria para NSStringde modo a adicionar um método para mostrar a versão em maiúsculas de uma string em um determinado ponto, chamando através de um dos métodos da categoria NSStringDrawing existentes para realizar o desenho real. Estes métodos estão documentados no NSString UIKit Adições de referência para iOS e NSString Aplicação Kit Adições de referência para OS X.

3 – Adicione duas propriedades readonly para a implementação original da classe XYZPessoa para representar a altura eo peso da pessoa, junto com métodos para measureWeight e measureHeight.

Use uma extensão de classe para redeclarar as propriedades como readwrite e implementar os métodos para definir as propriedades para os valores adequados.

Anúncios

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