Programando com Objective-C Parte 3

Ola Galera!

Aqui a terceira parte da tradução. Lembrando que se trata de uma tradução livre, feita de estudante para estudantes. Caso contenha algum erro ou tiverem sugestões será sempre bem vinda!

Para não viu ainda, aqui estão os links para a primeira e segunda parte:

Parte 1
Parte 2

Trabalhando Com Protocolos

No mundo real, as pessoas em assuntos oficiais são muitas vezes obrigados a seguir procedimentos rigorosos ao lidar com determinadas situações. Os policiais, por exemplo, são obrigados a “seguir o protocolo” ao fazer perguntas ou recolher provas.
No mundo da programação orientada a objetos, é importante ser capaz de definir um conjunto de comportamento que se espera de um objeto em uma dada situação. Como exemplo, uma exibição de tabela espera ser capaz de se comunicar com um objeto de fonte de dados, a fim de descobrir o que é necessário para exibir. Isto significa que a fonte de dados deve responder a um conjunto específico de mensagens que a exibição de tabela pode enviar.
A fonte de dados pode ser uma instância de qualquer classe, como um view controller (uma subclasse de NSViewController no OS X ou UIViewController no iOS) ou de uma classe de fonte de dados dedicado que talvez apenas herda NSObject. Para que a exibição de tabela saber se um objeto é indicado como uma fonte de dados, é importante ser capaz de declarar que o objeto implementa os métodos necessários.
Objective-C permite definir protocolos, que declaram os métodos que deverão ser utilizados para uma situação particular. Este capítulo descreve a sintaxe para definir um protocolo formal, e explica como marcar uma interface de classe, em conformidade com um protocolo, o que significa que a classe deve implementar os métodos necessários.

Protocolos Definem os Contratos de Mensagens

A interface da classe declara os métodos e as propriedades associadas a essa classe. Um protocolo, pelo contrário, é usado para declarar métodos e propriedades que são independentes de qualquer classe específica.
A sintaxe básica para definir um protocolo é parecido com isto:
@protocol NomeProtocolo
// lista de métodos e propriedades
@end
Os protocolos podem incluir declarações para ambos os métodos de instância e métodos de classe, bem como propriedades.
Como exemplo, considere uma classe de exibição personalizado que é usado para exibir um gráfico de pizza, como mostrado na figura:

A exibição personalizada de Gráfico Pizza

Para que a view seja reutilizável o quanto possível, todas as decisões sobre as informações devem ser deixadas para um outro objeto, uma fonte de dados. Isso significa que várias instâncias da mesma classe view poderia exibir informações diferentes apenas pela comunicação com diferentes fontes.
As informações mínimas necessárias pela view do gráfico inclui o número de segmentos, o ramanho relativo de cada segmento, e o título de cada segmento. O Protocolo de fonte de dados do gráfico de pizza, portanto, poderá parecer como este:
@protocol XYZFonteDadosGraficoPizza
-(NSUInteger) numeroDeSegmentos;
-(CGFloat) tamanhoDoSegmentoNoIndex:(NSUInteger)indexSegmento;
-(NSString *) tituloParaSegmentoNoIndex:(NSUInteger) indexSegmento;
@end
Nota: Este protocolo utiliza o valor NSUInteger para valores escalares inteiros não assinados. Este tipo é discutido em mais detalhes no capítulo seguinte.
A interface da classe chart view precisaria de uma propriedade para controlar o objeto de fonte de dados. Este objeto pode ser de qualquer classe, então o tipo da propriedade básico será id. A única coisa que se sabe sobre o objeto é que ele está em conformidade com o protocolo em questão.
A sintaxe para declarar a propriedade para a fonte de dados da view seria como:
@interface XYZGraficoPizzaView : UIView
@property (weak) id fonteDados;
@end
Objective-C usa sinais de (maior que) para indicar a conformidade com um protocolo. Este exemplo declara uma propriedade fraca (weak) para um ponteiro de objeto generico que está de acordo com o protocolo XYZGraficoPizzaViewFondeDados.
NOTA: Delegate e propriedades de fonte de dados são normalmente marcados como fracos para gerenciamento de objetos gráficos, como descrito anteriormente em “Anulando Ciclos de Referência Fortes”.
Ao especificar a conformidade do protocolo requerido na propriedade, você terá um aviso do compilador, se você tentar configurar a propriedade para um objeto que não está em conformidade com o protocolo, mesmo que o tipo básico da propriedade seja genério (ou seja tipo id). Não importa se o objeto é uma instancia de UIViewController ou NSObject. Tudo o que importa é que ele esteja de acordo com o protocolo, o que significa que a view do gráfico de pizza sabe que pode solicitar as informações que necessita.

Protocolos Podem Possuir Métodos Opcionais

Por padrão, todos os métodos declarados em um protocolo são métodos nescessários.
Isto significa que qualquer classe que está em conformidade com o protocolo deve implementar esses métodos.
Também é possível especificar métodos opcionais em um protocolo. Estes são os métodos que uma classe pode implementar somente se for necessário.
Como exemplo, você pode decidir que os títulos do gráfico de pizza deverão ser opcionais. Se o objeto da fonte de dados não implementa o titleForSegmentAtIndex:, sem títulos deve ser mostrada na view.
Você pode marcar métodos como protocolos opcionais usando a diretiva @opcional, assim:
@protocol XYZ XYZGraficoPizzaViewFondeDados
-(NSUInteger)numeroDeSegmentos;
-(CGFloat)numeroDeSegmentoNoIndex: (NSUInteger) segmentIndex;
@optional
– (NSString *) tituloParaSegmentoNoIndex:(NSUInteger) segmentoIndex;
@end
Neste caso, somente o tituloParaSementoNoIndex: é marcado como opcional. O método anterior não possui diretiva, então são assumidos como necessários.
A directiva @optional aplica-se a todos os métodos que se seguem, seja até o fim da definição do protocolo, ou até outra directiva for encontrada, tal como @requered. Você pode adicionar outros métodos para o protocolo como este:
@protocol XYZGraficoPizzaViewFondeDados
-(NSUInteger) numeroDeSegmentos;
-(CGFloat) tamanhoDoSementoNoIndex:(NSUInteger)segmentoNoIndex;
@optinal
-(NSStrig *)tituloParaSementoNoIndex: (NSUInteger)segmentIndex;
-(BOOL)deveEstourarSementoNoIndex:(NSUInteger)segmentIndex;
@required
-(UIColor *) corParaSementoNoIndex: (NSUInteger) segmentindex;
@end
Este exemplo define um protocolo com com 3 métodos requeridos e 2 métodos opcionais.

Verificar que Métodos Opcionais são Implementados em Tempo de Execução

Se um método de um protocolo é marcado como opcional, você deve verificar se um objeto implementa esse método antes de tentar chamá-lo.

Como exemplo, a view do gráfico de pizza pode testar o método do segmento título deste modo:

NSString *tituloDesteSegment;
if ([self.fonteDados respondsToSelector:@selector(tituloParaSegmentoNoIndex:)] {
tituloDesteSegment = [self.fondeDados tituloParaSementNoIndex:index];

}

O método respondToSelector: usa um selector, se refere ao identificador de um método após a compilação. Você pode fornecer o identificador correto usando a directiva @selector() e especificando o nome do método.
Se a fonte de dados, neste exemplo, implementa o método, o título é usado, caso contrário, o título permanece nula.
Importante:Variáveis ​​de objeto locais são automaticamente inicializados com nil.

Se você tentar chamar o método respondsToSelector: em um id em conformidade com o protocolo, como é definido acima, você obterá um erro de compilação, informando que não há uma método de instância conhecido para ele. Depois de qualificar um id com um protocolo, tudo estático tipo de checagem de retorno , você obterá um erro se você tentar chamar qualquer método que não está definido no protocolo especificado. Uma maneira de evitar o erro de compilador é definir o protocolo personalizado a adotar o protocolo NSObject.

Protocolos Herdam de Outros Protocolos

Do mesmo modo que uma classe Objective-C pode herdar de uma superclasse, você também pode especificar que um protocolo está de acordo com o outro.

Como exemplo, é a melhor prática definir seus protocolos em conformidade com o protocolo NSObject (alguns dos comportamentos NSObject está dividida a partir de sua interface de classe em um protocolo separado, a classe NSObject adota o protocolo NSObject).

Ao indicar que o seu próprio protocolo está de acordo com o protocolo NSObject, você está indicando que qualquer objeto que adota o protocolo personalizado também vai fornecer implementações para cada um dos métodos de protocolo NSObject. Porque você está presumivelmente usando alguma subclasse de NSObject, você não precisa se preocupar em fornecer suas próprias implementações para esses métodos NSObject. A adoção de protocolo é útil, contudo, para situações como a descrita acima.

Para especificar que um protocolo está de acordo com o outro, você fornece o nome do outro protocolo em sinais , assim:

@protocol MeuProtocolo
@end
Neste exemplo, qualquer objeto que adotar MeuProtocolo também efetivamente adota todos os métodos declarados no protocolo NSObject.

Conformidade com os protocolos

A sintaxe para indicar que uma classe adota um protocolo novamente usa os sinais , como abaixo:
@interface MinhaClasse: NSObject

@end

Isto significa que qualquer instancia de MinhaClasse irá responder não somente a métodos declarados especificamente na interface, mas que MinhaClasse tambem provê implementação para métodos requeridos em MeuProtocolo. Não será nescessário declarar novamente os métodos dos protocolos na interface da classe – a herança do protocolo é o suficiente.

Se nescessario uma classe adota multimplos protocolos, você pode espeficicá-los com uma lista separada por virgula, como abaixo:

@interface MinhaClasse: NSObject

@end

Dica: Se você está adotando um grande número de protocolos em uma classe, pode ser um sinal de que você precisa refatorar uma classe excessivamente complexa, dividindo o comportamento necessário em várias classes menores, cada um com responsabilidades claramente definidas.

Uma relativamente comum armadilha para novos desenvolvedores OS X e iOS é a utilização de uma única classe application delegate para conter a maioria das funcionalidades de um aplicativo (gerenciamento de estruturas de dados subjacentes, servindo os dados para vários elementos de interface do usuário, bem como responder a gestos e outros interação com o usuário). A complexidade aumenta, tornado a classe mais difícil de manter.

Depois de ter indicado a conformidade com um protocolo, a classe deve pelo menos fornecer implementações de métodos para cada um dos métodos de protocolo necessários, bem como quaisquer métodos opcionais que você escolher. O compilador irá avisá-lo se você não conseguir implementar qualquer um dos métodos necessários.

Nota: A declaração de método em um protocolo é igual a qualquer outra declaração. O nome do método e tipos de argumentos na implementação deve coincidir com a declaração no protocolo.

Cocoa e Cocoa Touch Definem um Grande Numero de Protocolos

Protocolos são usados por objetos Cocoa e Cocoa Touch para uma variedade de diferentes situações. Pro exemplo, as classes table view (NSTableView for OS X e UITableView para IOS) ambas usam objetos de fonte de dados para abastecê-la com a informação necessária. Ambos definem o seu proprio protocolo de fonde de dados, que é usado da mesma maneira como o protocolo XYZGraficoDePizzaFonteDeDados do exemplo anterior. Ambas as classes table view também permitem a você a configurar um objeto delegate, que novamente deve estar conforme o protocolo NSTableViewDelegate ou UITableViewDelegate. O delegate é responsável por lidar com interações do usuário, ou personalizar a exibição de determinadas entradas.

Alguns protocolos são usados ​​para indicar semelhanças não-hierárquicas entre as classes. Em vez de serem ligados a requisitos específicos de classe, alguns protocolos ao invés de se relacionar com mecanismos de comunicação mais generalizada Cocoa ou Cocoa Touch que podem ser adotadas por vários, as classes não relacionadas.

Por exemplo, muitos objetos modelo de framework (tal como uma coleçãõ~de classes como NSArray e NSDictionary) suportam o protocolo NSCoding, o que siginifica que eles podem codificar e decodificar suas propriedades para arquivamento ou distribuição como dados brutos.

NSCoding torna relativamente fácil escrever inteiros gráficos de objetos para o disco, desde que todos os objetos dentro do gráfico adotada o protocolo.

Alguns recursos de niveis de linguagens de Objective-C também contam com protocolos. Para utilizar a enumeração rápida por exemplo, uma coleção deve ser adotar o protocolo NSFastEnumeration, como descrito em “Enumeração Rápida Torna mais Fácil Enumerar uma Coleção.” Adicionamente alguns objetos podem ser copiados, tal como quando usamos uma propriedade com um atributo copy como descrito em “Copiar Propriedades Mantêm suas Próprias Cópias”. Qualquer objeto que você tentar copiar deve adotar o protocolo NSCopying, caso contrário, você vai ter uma exceção em tempo de execução.

Protocolos São Usados Para Anonimato

Protocolos também são úteis em situações em que a classe de um objeto não é conhecida, ou precisa ficar escondida.

Como exemplo, o desenvolvedor de um framework pode optar por não publicar a interface para uma das classes dentro do framework. Devido ao nome da classe ser desconhecida, não é possível que um usuário do framework crie uma instância dessa classe diretamente. Em vez disso, algum outro objeto no framework normalmente será designado para retornar uma instância ready-made (criada e pronta), como exemplo abaixo:

id utility = [frameworkObject anonymousUtility];

Para que este objeto anonymousUtility seja útil, o desenvolvedor do framework pode publicar um protocolo que revela alguns de seus métodos. Embora a interface de classe original não seja fornecido, o que significa que a classe permanece anónima, o objecto ainda pode ser utilizada de uma maneira limitada:

id utility =

[frameworkObject anonymousUtility];

Se você estiver escrevendo um app para IOS que usa o Core Data Framework, por exemplo, provavelmente você vai executar na classe NSFetchedResultsController. Esta classe é projetada para ajudar um objeto fonte de dados a fornecer dados já armazenados para um UITableView no IOS, tornando mais fácil fornecer informações como o número de linha.

Se você está trabalhando com uma table view cujo conteúdo é dividido em múltiplas seções, você pode também pedir a um controlador de resultado de busca por informações relevantes para a respectiva seção.

Ao invés de retornar uma classe especifica contendo informações da seção, a classe NSFetchedResultsController retornará um objeto anônimo, qe está em conformidade com o protocolo NSFetchedResultsSectionInfo. Isso significa que ainda é possível consultar o objeto para a informação que você precisa, como o número de linhas em uma seção:

NSInteger sectionNumber = …
id secInfo = [self.fetchedResultsController.sections objectAtIndex:sectionNumber];

NSInteger numberOfRowsInSection = [sectionInfo numberOfObjects];

Mesmo que você não sabe a classe do objeto sectionInfo, o protocolo NSFetchedResultsSectionInfo diz que ele pode responder à mensagem numberOfObjects.

Valores e Coleções

Apesar de Objective-C ser uma linguagem de programação orientada a objetos, é um super conjunto de C, o que significa que você pode usar qualquer um dos tipos escalares do padrão C (não-objeto) como tipos int, float e char no código Objective-C. Existem também outros tipos escalares disponíveis em aplicações Cocoa e Cocoa Touch, como NSInteger, NSUInteger e CGFloat, que têm definições diferentes, dependendo da arquitetura alvo (IOS ou OSX).

Tipos escalares são usads em situações onde voce não precisa dos benefícios (ou coisas associadas) de usar um objeto para representar um valor. Enquanto strings de caracteres são geralmente representadas como instancias da classe NSString, os valores numéricos são frequentemente armazenados em variáveis ou propriedades locais escalares.

É possível declarar uma matriz de estilo C em Objective-C, mas você verá que as coleções em aplicações Cocoa e Cocoa Touch geralmente são representados usando instâncias de classes como NSArray ou NSDictionary. Essas classes só pode ser usada para coletar (armazenar objetos já criados) objetos Objective-C, o que significa que você vai precisar criar instâncias de classes como NSValue, NSNumber ou NSString, a fim de representar os valores antes que você possa adicioná-los a uma coleção.

Os capítulos anteriores deste guia fazem uso freqüente da classe NSString e sua inicialização e da classe métodos de factory, bem como a literal Objective-C @”string”, que oferece uma sintaxe concisa para criar uma instância NSString. Este capítulo explica como criar NSValue e objetos NSNumber, usando chamadas de método ou através da sintaxe de valores literais de Objective-C.

Báciso C Tipos Primitivos estão Disponíveis em Objective-C

Cada tipo de variável escalar padrão C está disponível em Objective-C

int someInteger = 42;
float someFloatingPointNumber = 3.1415;

double someDoublePrecisionFloatingPointNumber = 6.02214199e23;

bem como operadores padrão C:

int someInteger = 42;
someInteger++; // someInteger == 43
someInteger–; // someInteger == 42

someInteger *= 2; // someInteger == 84

Se você usar um tipo escalar para uma PROPRIEDADE Objective-C, como aqui:

@interface XYZCalculator : NSObject

@property double currentValue;
@end
também é possível usar operadores C na propriedade quando for acessar o valor via sintaxe de ponto, assim:
@implementation XYZCalculator
– (void)increment {
self.currentValue++;
}
– (void)decrement {
self.currentValue–;
}
– (void)multiplyBy:(double)factor {
self.currentValue *= factor;
}
@end
A sintaxe de ponto é puramente uma sintático, um invólucro em torno de chamadas de método de assessor, para cada uma das operações neste exemplo é equivalente ao primeiro usando o método de acesso get para obter o valor, em seguida, realizar a operação, em seguida, usando o método acessor set para definir o valor para o resultado.


Objective-C Define Tipos Primitivos Adicionais

 

O tipo escalar BOOL está definido em Objective-C para mandar um valor Boleano, que pode ser YES ou NO. Como você pode observar, YES será logicamente equivalente a true e 1, enquanto NO é equivalente a false e 0.
Muitos parametros de métodos nos objetos de Cocoa e Cocoa Touch também usam tipos numéricos escalares especiais, tal como NSInteger ou CGFloat.
Por exemplo, os protocolos NSTableViewDataSource e UITableVideDataSource (descritos em um capitulo anterior) ambos possuem métodos de requerir o numero de linha para ser exibido:
@protocol NSTableViewDataSource
– (NSInteger) numberOfRowsInTableView:(NSTableView*)tableView;
@end
Estes tipos, como NSInteger e NSUInteger, são definidos diferentemente dependendo da arquitetura alvo. Quando a building for para ambiente 32-bit(tal como para IOS), eles serão inteiros de 32-bits sinalizados( “+” positivo, ou “-” negativo) e não-sinalizados, respectivamente; Quando a building é para ambiente de 64-bit (tal como tempo de execução dos modernos OS X) eles serão inteiros de 64-bit sinalizados( “+” positivo, ou “-” negativo) e não-sinalizados, respectivamente.
Esta é a melhor prática para usar estes tipos específicos de plataforma, se voce puder passar valore através dos limites da API, (ambos, APIs internas e exportadas), como argumentos ou valores de retorno do método ou chamadas de funções entre o código de sua aplicação e um framework.
Para variáveis locais, como um contador de looping, não há problema em usar os tipos básicos de C, se você sabe que o valor está dentro dos limites normais.


Estruturas C Podem Guardar Valores Primitivos

 

Algumas API em Cocoa e Cocoa Toucj usam estruturas C para guardar seus valores. Como exemplo, é possivel perguntar a um objeto string o intervalo de uma substring, assim:
NSString *mainString = @”This is a long string”;
NSRange substringRange = [mainString rangeOfString:@”long”];
Uma structure NSRange guarda uma localização e comprimento. Neste caso, substringRage irá guardar um intervalo de {10,4} – o “L” no início de @”long” é o caractere no índice com base zero, sendo a posição 10 em mainString, e @”long” possui o comprimento de 4 caracteres.
Similarmente, se voce precisar escrever um código para desenhar, você precisará interagir com o Quartz, que requer structures que são baseadas em torno de tipo de dados de CGFloat, como NSPoint e NSSize no OS X e CGPoint e CGSize no IOS> Novamente, CGFloat é definido de forma diferente dependendo da plataforma alvo.
Para mais informações sobre desenhar 2D no Quartz engine, veja Guia de Programação 2D Quartz.

Objetos Podem Representar Valores Primitivos

Se você precisa representar um valor escalar como objeto, tal como quando estiver trabalhando com coleção de classes descritas na próxima seção, você pode usar uma das classes de valores básicos fornecidos por Cocoa e Cocoa Touch.

 

Strings São Representadas por Instâncias de Classe NSString

 

Como você pode ver nos capítulos anteriores, NSString é usado para representar uma string de caracteres, como Hello World. Existem varios modo de criar objetos NSString, incluindo alocação e inicialização padrão, métodos de classes fábrica, ou sintaxe literal:
NSString * primeiraString = [[NSString alloc] initWithString:”Hello World!” encoding:NSUTF8StringEncoding];
NSString *segundaString = [NSString stringWithCString: “Hello World!” encoding:NSUTF8StringEncoding];
NSString = terceiraString = @”Hello World!”;
Cada um destes exemplos efetivamente realiza a mesma coisa – cria um objeto de string que representa o mesmos caracteres fornecidos.
A basse da classe NSString é imutábel, o que siginifica que este conteúdo são criados e configurados e não podem ser alterados. Se voce precisar representar uma diferente string, você deverá crirar um novo objeto de string, como abaixo:
NSString *name = @”Jhon”;
name = [name stringByAppendingString:@”ny”];
// retorna um novo objeto string
A classe NSMutableString é uma subclasse mutável de NSString, e permite voce alterar os caracteres contidos em tempo de execução usando métodos como appendString: ou appendFormat:, como no exemplo:
NSMutableString *name = [NSMUtableString stringWithString:@”John”];

[name appendString:@”ny”]; //O mesmo objeto, mas agora repesentando “Johnny”.

 

Formatação De Strings São Usados ​​Para Construir Strings A Partir De Outros Objetos Ou Valores

Se voce precisar construir uma string contendo valores variáveis, você precisará trabalhar com um format string. Isso permite que você use formato específicos para indicar como o valor será inserido, ou seja:

int magicNumber = …
NSString *magicString = [NSString stringWithFormat:@”The magic number is %i: “, magicNumber];
Os formatos especificadores disponíveis estão descritos em “Formato Especificador de Strings”. Para mais informações sobre strings em geral, veja “Guia de Programação de String


Números São Representados por Instâncias da Classe NSNumber

 

A classe NSNumber é usado para representar qualquer tipo básico escalar de C, incluindo char, double, float, int, ling, short e as variáveis unsigned (sem sinal – ou seja valor modular) de cada, bem como tipo Boleano de Obj-C, BOOL.
Tal como NSString, voce terá várias ooções para criar instancias de NSNumber, como alocação e inicialização ou métodos de classes fábrica:
NSNumber *magicNumber = [[NSNumber alloc] initWithInt:42];
NSNumber *unsignedNumber = [[NSNumber alloc] initWithUnsignedInt:42u];
NSNumber *longNumber = [[NSNumber alloc] initWithLong:42l];
NSNumber *boolNumber = [[NSNumber alloc] initWithBOOL:YES];
NSNumber *simpleFloat = [NSNumber numberWithFloat:3.14f];
NSNumber *betterDouble = [NSNumber numberWithDouble:3.1415926535];
NSNumber *someChar = [NSNumber numberWithChar:’T’];
Isto também possibilita criar instancias de NSNumber usando sintaxe literal de Objective-C:
NSNumber *magicNumber = @42;
NSNumber *unsignedNumber = @42u;
NSNumber *longNumber = @42l;
NSNumber *boolNumber = @YES;
NSNumber *simpleFloat = @3.14f;
NSNumber *betterDouble = @3.1415926535;
NSNumber *someChar = @’T’;
Estes exemplos são o equivalente a usar a métodos de fábrica classe NSNumber.
Depois de criar uma instância NSNumber é possível solicitar o valor escalar usando um dos método de acesso:
int scalarMagic = [magicNumber intValue];
unsigned int scalarUsigned = [unsignedNumber unsignedIntValue];
long scalarLong = [longNumber longValue];
BOOL scalarBool = [boolNumber boolValue];
float scalarSimpleFloat = [simpelFloat floatValue];
double scalarBetterDouble = [betterDouble doubleValue];
char scalarChar = [someChar charValue];
A classe NSNumber também oferece métodos para trabalhar com tipos primitivos adicionais de Obj-C. Se você precisar criar uma representação de objeto escalar dos tipos NSInteger e NSUInteger, por exemplo, certifique-se de usar os métodos corretos:
NSInteger anInteger = 64;
NSUInteger anUnsignedInteger = 100;
NSNumber *firstInteger = [[NSNumber alloc] initWithInteger:anInteger];
NSNumber *secondInteger = [NSNumber numberWithUnsignedInteger:anUnsignedInteger];
NSInteger integerCheck = [firstInteger integerValue];
NSUInteger unsignedCheck = [secondInteger unsignedIntegerValue];
Todas as instancias de NSNumber são imutávels, e não há subclasse mutável; Caso você precise de um número diferente, use outra instância de NSNumber.
Nota: NSNumber é na verdade um conjunto de classes. Isto significa que quando você cria uma instância em tempo de execução, você vai ter uma subclasse concreta adequado para armazenar o valor fornecido. Apenas tratar o objeto criado como uma instância de NSNumber.

Representando Outros Valores Usando Instancias Da Classe NSValue

 

A classe NSNumber é por sí so uma subclasse da classe básica NSValue, que fornece um objeto de invólucro em torno de um único valor ou item de dados. Em adição ao tipos básicos escalares de C, NSValue pode tambem ser usado para representar ponteiros e structures.
A classe NSValue oferece vários métodos fábricas para criar um valor para criar uma estrutura com um valor padrão de dado, o que faz com que seja fácil de criar uma instância para representar, por exemplo, um NSRange, como no exemplo do capítulo anterior:
NSString *mainString = @”This is a long string”;
NSRange substringRange = [mainString rangeOfString:@”long”];
NSValue *rangeValue = [NSValue valueWithRange: substringRange];
Também é possível criar objetos NSValue para representar estruturas personalizadas. Se você tem uma necessidade específica de usar uma structure de C (ao invés de um objeto Objective-C) para armazenar informações, como esta:
typedef struct {
int i;
float f;
} MyIntegerFloatStruct;

Você pode criar uma instância NSValue fornecendo um ponteiro para a estrutura, bem como um tipo Objective-C codificado. A diretiva do compilador, @encode(), é usada para criar o tipo de objeto correto em Objective-C, como este:
struct MyIntegerFloatStruct aStruct;
aStruct.i = 42;
aStruct.f = 3.14;
NSValue *structValue = [NSValue value:&aStruct withObjCType:@encode(MyIntegerFloatStruct)];
O operador de referencia padrão C é usado para fornecer o endereço da aStruct para o parâmetro value.

A Maioria das Coleções São Objetos

 

Embora seja possível a utilização de uma matriz C para manter uma coleção de valores escalares, ou mesmo ponteiros de objeto, a maioria das coleções em código Objective-C são exemplos de uma das classes de coleção Cocoa Touch e Cocoa, como NSArray, NSSet e NSDictionary.
Essas classes são usadas para gerenciar grupos de objetos, o que significa que qualquer item que você deseja adicionar a uma coleção deve ser uma instância de uma classe Objective-C. Se você precisa adicionar um valor escalar, você deve primeiro criar uma instancia adequada de NSNumber ou NSValue para representá-lo.
Ao invés de alguma forma, mantem uma cópia separada de cada objeto coletado, as classes de coleção usam referências fortes para manter o controle de seus conteúdos. Isto significa que qualquer objeto que você adicionar a uma coleção será mantido vivo, pelo menos enquanto a coleção é mantida viva, conforme descrito no “Gerenciar o Gráfico do Objeto Através da Propriedade e Responsabilidade”.
Além de manter o controle de seus conteúdos, cada uma das classes de coleção Cocoa Cocoa Touch se torna mais fácil para executar determinadas tarefas, tais como a enumeração, acessando itens específicos, ou descobrir se um determinado objeto é parte da coleção.
A classe básica NSArray, NSSet e NSDictionary são imutáveis, o que siginifica que os seus conteúdos são definidos no momento da criação. Cada um também tem uma subclasse mutável para permitir qe você adicione ou remova objetos à vontade.
Para mais informações sobre diferentes classes de coleções disponíveis em Cocoa e Cocoa Touch, veja Tópicos De Programação de Coleções.

Arrays São Coleções Ordenadas.

 

Um NSArray é usado para representar uma coleção ordenada de objetos. A única exigência é que cada item seja um objeto Objective-C – não há nenhuma exigência para que cada objeto seja uma instância da mesma classe.
Para manter a ordem no array, cada elementp é armazenado a partir do index zero, como mostra a figura a seguir:


 

 

Criando Arrays

 

Tal como acontece com as classes de valor descritos no início deste capítulo, você pode criar uma matriz através da alocação e inicialização, métodos de fábrica de classe, ou sintaxe literal.
Estes são uma variedades de diferentes métodos de inicialização e fábrica disponíveis, dependendo do numero de objetos:
+(id)arrayWithObject:(id)anObjeto;
+(id)arrayWithObjects:(id)primeiroObjeto,…;
-(id)initWithObjects:(id)primeiroObjeto,…;
Os métodos arrayWithObjects: e initWithObject: ambos recebem uma terminação com nil, devido ao número variável de argumentos, oque siginifica que devemos incluir o nil como ultimo valor, para informar o fim de argumentos que estamos inserindo, como no exemplo:
NSArray *someArray = [NSArray arrayWithObjects: someObject, someString, someNumber, someValue, nil];
Neste exemplo cria um arrai como é mostado anteriormente, na figura anterior. O primeiro objeto, someObject, deverá ter o endereço no index no array 0 (zero), o ultimo objeto, someValue, irá ter o index 3.
É possível truncar a lista de itens involuntariamente se um dos valores fornecidos ser nil, como no exemplo:
id firstObject = @”someString”;
id secondObject = nil;
id thirtObject = @”anotherString”;
NSArray *someArray = [NSArray arrayWithObjects:firstObject, secondObject, thirtObject];
Neste caso, someArray conterá somente o firstObject, porque o secondObject nil será interpretado como o fim da lista de itens.

Sintax Literal

 

É tambem possivel criar um array usando um literal Obj-c, como a seguir:
NSArray *someArray = @[firstdObject, secondObject, thirdObject];
Você não deverá terminar a lista de objetos com nil quando usar a sintax literal, e neste caso nil será um valor inválido. Você receberá uma exceção em tempo de execução se voce tentar executar o seguinte código:
id firstObject = @”someString”;
id secondObject = nil;
NSArray *someArray = @[firstObject, secondObject];
//exceção: “Tentativa forçada de inserir objeto nil”
Se você precisar representar um valor nil em uma coleção de classes, você deverá usar a classe singleton NSNull, como descrito em “Representar nil com NSNull”.

Consultando Objetos de Arrays

 

Desde que você criou um arra, você pode consultá-lo você para objeter infomações como número de objetos, ou se eles contém um determinado item:
NSNInteger numberOfItems = [someArray count];
if ([someArray containsObject:someString]) {
}
Você também pode consultar o array para m item em um determinado index. Você receberá uma exceção out-of-bound em tempo de execução se você tentar solicitar um índice inválido, por isso você deve sempre verificar o número de itens primeiro.
if ([someArray count] >0) {
NSLog (@”Primeiro Item é:%@”, [someArray objectAtIndex:0]);
}

Subscripting

 

Há também uma sintaxe subscript alternativa, para usar objectAtIndex, que é o modo de acessar um valor em uma matriz no padrão C. O exemplo anterior poderia ser re-escrito da seguinte maneira:
if ([someArray count] > 0) {
NSLog (@”First item is: %@”, someArray[0]);
}


Ordenando Arrays de Objetos

 

A classe NSArray também oferece uma variedade de métodos para ordenar a colação de objetos. Portanto NSArray é imutável, cada um desses métodos retorna um novo array contendo os itens na ordem de classificação.
Como um exemplo, você pode organizar um array de strings pelo resultado da chamada ao método compare: em casa string, como no exemplo:


Mutabilidade

 

Embora a classe NSArray em si é imutável, isso não tem qualquer influência em todos os objetos recolhidos. Se você adicionar uma string mutável para uma matriz imutável, por exemplo, assim:
NSMutableString *mutableString = [NSMutableString stringWithString:@”Hello”];
NSArray *immutableArray = @[mutableString];
não há nada para impedi-lo de mutar a string:
if([immutableArray count] > 0) {
id string = immutableArray[0];
if ([string isKindOfClass: [NSMutableString class]]) {
[string appendString:@” World”];
}Esse exemplo cria um array que terminará com o objeto @”epsilon”, @”alpha”, @”beta!. Também é possível classificar um array mutável no lugar, sem criar outro array secundário:[mutableArray sortUsingSelector:@selector(caseInsensitiveCompare:)];

Conjuntos São Coleções Desordenadas

 

Um NSSet é parecido com um array, mas mantém um grupo de objectos não ordenada distintos, como mostrado na imagem abaixo:



Portanto conjuntos não conservam a ordem, oferecem uma melhoria de desempenho acima de array, quando se trata de testar para a associação.
A classe básica NSSet é também imutável, assim o seu conteúdo deve ser especificado na criação, utilizando alocação e inicialização ou um método de fábrica de classe, assim:
NSSet *simpleSet = [NSSet setWithObjects:@”Hello, World!”, @42, aValue, anObject, nil];
Assim como NSArray, os métodos initWithObjects: e setWithObjects:, ambos recebem um finalizador nil e um número variável de argumentos. A subclasse mutável de NSSet é NSMutableSet.
Conjuntos apenas armazenam uma referência para um objeto individual, mesmo se você tentar adicionar um objeto mais de uma vez:
NSNumber *number = @42;
NSSet *numberSet = [NSSet setWithObjects:number, number, number, nil];
//numberSet somente conterá um objeto
Para mais informações sobre Conjuntos, veja em “Conjuntos: Coleção Desordenada de Objetos”.

Dicionários Que Coletam Pares de Chave-Valor (Key-Value)

 

Em vez de simplemente manter um conjunto de objetos ordenados ou não ordenados, um objeto NSDIctionary armazena objetos com chaves (Keys), que podem ser usadas para reaver o valor armazenado.
A melhor prática é usar um objeto de string como key (chave) do dicionário, como é mostrado na imagem abaixo:
Um Dicionário de Objetos:



Nota: É possivel usar outros objeto como keys, mas é importante notar que cada key é copiado para ser usada pelo o dicionário e por isso deve suportar NSCopying.
Se você deseja ser capaz de utilizar a Key-Value Coding, no entanto, como descrito em Guia de Programação Key-Value Coding, você deve usar chaves string para o dicionário de objetos.

Criar Dicionários

 

Você pode criar dicionários tanto alocação e inicialização, ou métodos de fábrica, como este exemplo:
NSDictionary *dictonary = [NSDictionary dictionaryWithObjectsAndKeys:
someObject, @”anObjet”,
@”Hello, World!”, @”helloString”,
@42, @”magicNumber”,
someValue, @”aValue”,
nil];
Note que para os métodos dictionaryWithObjectsAndKeys: e initWithObjectsAndKeys:, cada objeto é especificado antes da chave (key), em novamente, a lista de objeto e chaves deve ser terminada com nil.

Sintaxe Literal

 

Objective-C também oferece uma sintaxe literal para criar dicionário, como no exemplo:
NSDictionary *dictionary = @{
@”anObject” : someObject,
@”helloString” : @”Hello, World!”,
@”magicNumber” : @42,
@”aValue” : someValue
};
Note que para dicionários literais, a chave é especificada antes do objeto e não é terminada com nil.

Consultando Dicionários

 

A partir do momento que você tenha criado um dicionário, você pode perguntar por algum objeto armazenado de acodo com a chave atribuída a ele, como no exemplo:
NSNumber *storedNumber = [dictionary objectForKey: @”magicNumber”];
Se o objeto não é encontrado, o método objectForKey: retornará um nil;
Há também uma sintaxe alternativa subscript para usar objectForKey:, que se parece com isso:
NSNumber *storedNumber = dictionary[@”magicNumber”];


Mutabilidade

 

Caso você precise adicionar ou remover objetos de um dicionário depois de sua criação, você irá precisar usar a subclasse NSMutableDictionary:, como no exemplo:
[dictionary setObject:@”another string” forKey:@”secondString”];
[dictionary removeObjectForKey:@”anObject”];

Representando Nil Com NSNull

 

Não é possivel adicionar nil na coleção de classes descrita nesta seção por que nil em Objective-C significa “sem objeto”. Se você precisar representar um “não objeto” em uma coleção, voce pode usar a classe NSNull:
NSArray *array = @[@”string”, @42, [NSNull null]];
NSNull é uma classe singleton, que siginifica que o método null sempre retornará a mesma instancia. Isso significa que você pode verificar se um objeto em um array é igual à instância NSNull compartilhada:
for (id object in array) {
if(object == NSNull null]) {
NSLog(@”Objeto Null Encontrado”);
   }
}

Use Coleções Para Persistir Seu Objeto Gráfico

 

As classes NSArray e NSDictinary tornar fácil escrever seu conteúdo diretamente para o disco, parecido com isto:
NSURL *fileURL = …
NSArray *array = @[@”first”, @”second”, @”third”];
BOOL success = [array writeToURL:fileURL atomically: YES];
if(!success) {
NSLog (@”Ocorreu um Erro”);
}
Se todo objeto contido é um dos tipos de lista de propriedade (NSArray, NSDictionary, NSString, NSData, NSDate e NSNumber), é possível recriar toda a hierarquia do disco, como esta:
NSURL *fileURL = …
NSArray *array = [NSArray arrayWithContentsOfURL: fileURL];
if(!array) {
NSLog (@”Ocorreu um Erro”);
}
Para mais informações da lista de propriedades, veja Guia Programação da Lista de Propriedades. Se você precisar persistir outros tipos de objetos do que apenas as classes de lista de propriedade padrão mostrado acima, você pode usar um objeto arquivador, como NSKeyedArchiver, para criar um arquivo de objetos colecionados.
O único requisito para criar objetos arquivados é que cada objeto deve suportar o protocolo NSCoding. Isto significa que cada objeto deve saber como codificar-se a um arquivo (através da aplicação do método encodeWithCoder:) e decodificar-se quando ler a partir de um arquivo existente (o método initWithCoder:).
As classes NSArray NSSet e NSDictionary e suas subclasses mutáveis, todos suportam NSCoding , o que significa que você pode persistir complexas hierarquias de objetos usando um arquivador. Se você usar o Interface Builder para estabelecer as janelas e views, por exemplo, o arquivo nib resultante é apenas um arquivo da hierarquia de objetos que você criou visualmente. Em tempo de execução, o arquivo nib é desarquivado a uma hierarquia de objetos usando as classes relevantes.
Para mais informações sobre Arquivadores, veja Guia de Programação de Arquivadores e Serialização.


Use as Técnicas de Enumeração de Coleção Mais Eficientes

 

Objective-C eCocoa ou Cocoa Touch oferecem uma variedade de modos para enumerar o conteudo de uma coleção. Embora seja possivel usar um tradicional loop “for” de C para iterar todo o conteudo, como no exemplo:
int countArray = [array count];
for( int index = 0; index < countArray; index++){
id eachObject = [array objectAtIndex: index];
}
Esta é a melhor pratica de usar uma das técnicas descritas nesta seção.


Enumeração Rápida Torna Mais Fácil Enumerar Uma Coleção

 

Muitas classes de coleção estam em conformidade com o protocolo NSFastEnumeration, incluindo NSArray, NSSet e NSDictionary. Isso significa que você pode usar enumeração rápida, um recurso de nível da linguagem Objective-C.
A sintaxe de enumeração rápida para enumerar o conteúdo de um array ou conjunto é como esta:
for ( in ) {
}
Como no exemplo, voce pode usar enumeração rápida para registrar uma descrição de cada objeto contido no array, como abaixo:
for( id eachObject in array) {
NSLog(@”Object: %@”, eachObject);
}
A variável eachObject é ajustada automaticamente para o objecto atual a cada passagem pelo loop, assim uma declaração de registro aparece por objeto.
Se você usar rápida enumeração com um dictionary, você iterar sobre as chaves do dicionário, como este:
for (NSString *eachKey in dictionary) {
id object = dictionary[eachKey];
NSLog(@”Objecto: $@ for Key:%@, object, eachKey);
}
Enumeração Rápida, funciona como o loop “for” padrão em C, então você pode usar a palavra chave break para interromper a iteração, ou continue para avançar para o próximo elemento.
Se você está enumerando uma coleção ordenada, a enumeração prossegue na mesma ordem. Para um NSArray, isso significa que a primeira passagem será para o objeto no index 0, o segundo objeto para o index 1, etc. Se voce precisar manter o controle do index atual, faça uma contagem simples das iterações à medida que ocorrem.
int index = 0;
for (id eachObject in Array) {
NSLog(@”Object at index %i is: %@”, index, eachObject);
index++;
}
Você não pode mudar o valor de uma coleção durante a Enumeraçaõ Rápida, mesmo se a coleção for mutável. Se você tentar adicionar ou remover um objeto armazenado dentro do loop, você vai gerar uma exceção de tempo de execução.


A Maioria das Coleções Suportam Também a Enumeração de Objetos

 

Também é possivel enumerar muitas coleções de Cocoa e Cocoa Touch usando um objeto NSEnumerator.
Você pode pedir um NSArray, por exemplo, para um objectEnumerator ou um reverseObjectEnumerator. Também é possivel usar estes objetos com Rápida Enumeração, como abaixo:
for (id eachOject in [array reverseObject Enumerator]) {
….
}
   Neste exemplo, o loop irá repetir toda a coleção de objetos em ordem reversa, então o ultimo objeto será o primeiro, e assim por diante.Também é possível iterar percorrer o conteúdo chamando o método de enumeração nextObject repedidamente, como abaixo:
id eachObject;
while ( (eachObject = [enumerator nextObject])) {
NSLog(@”O objecto atual é: %@, eachObject);
}
Neste exemplo, um loop while é usado para ajutar a variável eachObject com o proximo objeto para cada passagem ao longo do loop. Quando não houver mais objetos retornados, pelo método nextObject, então será retornado nil, que será avaliado como um valor lógico FALSE para que o ciclo seja encerrado.
NOTA: É um erro comum de programação usar o operador de atribuição C ( = ) quando na verdade você quer dizer o operador de igualdade ( == ), o compilador irá avisá-lo se você definir uma variável em um bifurcação condicional ou loop, como no exemplo:
if(someVariavel = YES){ // Está errado
}
Se você realmente quer dizer para atribuir uma variável ( o valor lógico da atribuição em geral é o valor no final do lado esquerdo), você pode indicar isso colocando a atribuição entre parenteses, como no exemplo:

if( (someVariavel = YES) ){
}

Tal como acontece com a enumeração rápida, você não pode mutar uma coleção ao enumerar. E, como você pode reunir a partir do nome, é mais rápido usar enumeração rápida do que usar um objeto enumerador manualmente.

Muitas Coleções Suportam a Enumeração Baseada em Bloco (Block-Based)

 

Também é possivel enumerar NSArray, NSSet e NSDictionary usando blocos. Blocos são cobertos em detalhes no próximo capítulo.

Trabalhando Com Blocos

 

Uma classe Objective-C define um objeto que combina dados com comportamento relacionado. Às vezes, faz sentido apenas representar uma única tarefa ou unidade de comportamento, ao invés de uma coleção de métodos.
Os blocos são um recurso de nível de linguagem adicionado ao C, Objective-C e C + +, que permitem criar diferentes segmentos de código que podem ser passados em torno de métodos ou funções como se fossem valores. Blocos são objetos Objective-C, o que significa que pode ser adicionado a coleções como NSArray ou NSDictionary. Eles também têm a capacidade de capturar os valores do escopo encapsulado, tornando-os semelhantes aos encapsulamentos ou lambdas em outras linguagens de programação.
Este capítulo explica a sintaxe para declarar e se referir aos blocos, e mostra como usar blocos para simplificar as tarefas comuns, como enumerar coleções. Para mais informações, consulte Tópicos de Programação com Blocos.

Sintaxe de Block

 

A sintaxe para definir um bloco literal usa o simbolo Acento Circunflexo ( ^ ), como abaixo:
^{
NSLog (@“Isso é um bloco”);
}
Assim como definimos funções e métodos, as chaves indicam o começo e o fim dos blocos. Neste exemplo, o bloco não retorna nenhum valor, e não recebe nenhum argumento.
Do mesmo modo que você pode usar a função ponteiro para se referir a uma função C,você pode declarar uma variável para manter o controle de um bloco, como abaixo:
void (^simpleBlock) (void);
Se você não está acostumado a lidar com ponteiros de função C, a sintaxe pode parecer um pouco incomum. Este exemplo declara uma variável chamada simpleBlock para se referir a um bloco que não tem argumentos e não retorna um valor, o que significa que a variável pode ser atribuído ao bloco literal mostrado acima, como abaixo:
simpleBlock = ^{
NSLog(@”Isso é um bloco”);
};
Isso é apenas como qualquer outra atribuição de variável, de modo que a declaração deve terminar com um ponto e virgula depois de fechar as chaves. Você pode também combinar a declaração de variável com a atribuição:
void (^simpleBlock)(void) = ^{
NSLog(@”Isso é um bloco”);
};
Desde que você tenha declarado e atribuido uma variável de bloco, voce pode usá-la para invocar o bloco:
simpleBlock();
Nota: Se você tentar invocar um bloco usando uma variável ainda não atribuída (uma variável de bloco vazia, nil), seu aplicativo irár dar erro.

Blocos Recebem Argumentos e Retornam Valores

 

Blocos também recebe argumentos e retornam valores assim como métodos e funções.
Como exemplo, considere uma variável para se referir a um bloco que retona o resultado da multiplicação de dois valores:
double (^multiplyTowValues) (double,double);
O bloco correspondente literal pode ter esta aparência:
^ (double firstValue, double secondValue) {
return firstValue * secondValue;
}
O firstValue e secondValue são usador para se referir aos valores armazenado quando o bloco é invocado, como qualquer definição de função. Neste exemplo, o tipo de retorno é determinado a partir a declaração de retorno dentro do bloco.
Se você preferir, você pode fazer o tipo de retorno explicito, especificando-o entre o acento circunflexo e a lista de argumentos:
^double(double firstValue, double secondValue)
{
return firstValue * secondValue;
}
Depois de ter declarado e definido o bloco, você pode chamá-lo exatamente como se fosse uma função:
double (^multiplyTwoValues) (double, double) = ^(double firstValue, souble secondValue) {
return firstValue * secondValue;
};
double result = multiplyTwoValues(2,4);
NSLog(@”The result is %f”, result);


Os Blocos Podem Capturar Valores a Partir de Escopo Delimitado

 

Assim como contém o código executável um bloco também tem a capacidade de capturar o estado a partir do seu escopo delimitador.
Se você declarar um bloco literal a partir de dentro de um método, por exemplo, é possível capturar qualquer valor acessível dentro do escopo do método, como no exemplo:
– (void) testMethod (
int anInteger = 42;
void (^testBlock) (void) = ^{
NSLog(@” Integer is: %i “, anInteger);
};
testBlock();
}
Neste exemplo, anInteger é declarado fora do blocom nas o valor dele é capturado quando o bloco é definido. Somente o valor é capturado, a menos que você especifique o contrário. Isso siginifica que se você mudar o valor externo da variável entre o momento de definição do bloco e o momento de invocá-lo, como no exemplo abaixo, o valor capturado no bloco não será afetado. Isto siginifica que o registro que será impresso na tela ao invocar o bloco será 42.
int anInteger = 42; // Declaração de Variável com valor.
void (^testBlock) (void) = ^{ //Criação do bloco
NSLog(@”Integer is: %i:”, anInteger); // Atribuição do valor da variável
// do método para o bloco
};
anInteger = 84; // Nova atribuição de valor para a variável loca do método
testBlock(); // Invocação do bloco.
Saída do Bloco No Compilador: Integer is: 42


Utilize Variáveis __ bloco para Compartilhar Armazenamento

 

Se você precisa ser capaz de alterar o valor de uma variável capturada dentro de um bloco, você pode usar o modificador de tipo de armazenamento em __bloco na declaração original da variável. Isso significa que a vida de uma variável ​​em armazenamento que é compartilhada entre o escopo lexical da variável original e qualquer um dos blocos declarados dentro desse escopo.
Como exemplo, você pode reescrever o exemplo anterior da seguinte forma:
__block int anInteger = 42;
void (^testBlock) (void) = ^{
NSLog(@”Integer is: %i”, anInteger);
};
anInteger = 84:
testBlock();
Portanto anInteger é declarado como uma variável __block, essa variável armazenada é compartilhada com a declaração do bloco. Isso siginifica que a saida do registro deverar agora mostrar:
Integer is: 84;
Isto também siginifica que o bloco pode alterar o valor original?
__block inte anInteger = 42;
void (^testBlock)(void) = ^ {
NSLog(@”Integer is: %i”, anInteger);
anInteget = 100;
};
testBlock();
NSLog(@”Valor da variável original é agora: %i”, anInteger);
Desta vez a saída mostrada assim:
Integer is: 42
Valor da variável original é agora: 100


Você Pode Passar Blocos Como Argumentos para Métodos Ou Funções

 

Cada um dos exemplos anteriores neste capítulo invoca o bloco imediatamente depois de definí-lo. Na prática, é comum passar bloco para funções ou métodos para invocá-los em outro lugar. Você pode usar o Grand Central Dispatch para invocar um bloco em segundo plano, por exemplo, ou definir um bloco para representar uma tarefa para ser invocado repedidamente, tal como quando enumeramos uma coleção. Simultaneidade e enumeração são tratados mais tarde neste capítulo.
Blocos também são usados para retornos, definindo o código para ser executando quando uma tarefa está completa. Como exemplo, seu app poderá precisar responder a uma ação do usuário, criando um objeto que executa uma tarefa complicada, tal como solicitar informações a partir da internet ou serviço web. Portanto a tarefa poderá levar muito tempo, você deverá mostrar algum tipo de indicador de progresso enquanto a tarefa é efetuada, e então esconder esconder o indicador no momento que a tarefá estiver completa.
Seria possível fazer isso usando delegação: Você precisaria criar um protocol delegate adequado, implementar o método necessário, definir o seu objeto como o delegate da tarefa, em seguida, esperar que ele chame um método delegate em seu objeto uma vez que o tarefa terminou.
Blocos de fazer isso muito mais fácil, no entanto, você pode definir o comportamento da retorno de chamada no momento em que iniciar a tarefa, como este:
– (IBAction) fetchRemoteInformation: (id)sender {
[self showProgressIndicator];
XYZWebTask *task = …
[task beginTaskWithCallbackBlock: ^ {
[self hideProgressIndicator];
}];
}
Esse exemplo chama um método para mostrar o indicador de progresso, então cria uma tarefa e diz para iniciá-la. O bloco de retorno especifica o código que será executado a partir do momento que a tarefa for concluída; neste caso, é simplesmente chamar um método para ocultar o indicador de progresso. Note que este bloco de retorno captura self de modo a ser capaz de chamar o método hideProgressIndicator quando invocado.
É importante tomar cuidado ao capturar self porque é fácil criar um forte ciclo de referência, conforme descrito posteriormente em “Anulando Ciclos de Referencia Forte Quando Capturar Self”.
Em termos de legibilidade de código, o bloco torna fácil de ver em um lugar exatamente o que vai acontecer antes e depois da tarefa concluída, evitando a necessidade de traçar, através de métodos de delegate para descobrir o que vai acontecer.
A declaração para o método begiTaskWithCallbackBlock: mostrado neste exemplo ficaria assim:
-(void)beginTaskWithCallbackBlock: (void (^) (void) callbackBlock;
O (void (^) (void)) especifica que o parametro é um bloco que não receberá qualquer argumento ou retorna qualquer valor. A implementação do método pode invocar o bloco de modo usual:
-(void)beginTaskWithCallbackBlock: (void (^) callbackBlock {
callbackBlock();
}
Os parâmetros do método esperam que um bloco com um ou mais argumentos são especificados do mesmo modo como um bloco variável:
-(void)doSomethingWithBlock:(void) (^) (double,double))block {
block(21.0,2.0);
}

Um Bloco Deve Ser Sempre o Último Argumento Para um Método

 

-(void) beginTasckWithName:(NSString *) name completion:(void (^)(void))callback;
Issto faz com que seja mais facil de ler a chamada ao método ao especificar a linhda do bloco, como abaixo:
[self beginTaskWithName:@”MyTask” completion:^ {
NSLog(@”The Task Is Complete”);
}];

Use Definições De Tipo Para Simplificar A Sintaxe do Bloco

 

Se você precisar definir mais mais do que um bloco com a mesma assinatura, você poderá gostar de definir seu próprio tipo para a assinatura.
Como exemplo, você pode definir um tipo para um bloco simples sem argumentos ou sem retorno de valor, como abaixo:
typedef void (^XYZSimpleBlock) (void);
Você pode então usar seu próprio tipo customizado para parâmetros de métodos ou quando criar uma variável bloco.
XYZSimpleBlock anotherBlock = ^ {
};
——————————
-(void) beginFetchWithCalbackBlock: (XYZSimpleBlock)callbackBlock {
callbackBlock();
}
Definições de Tipo customizados são particularmente úteis quando tratamos blocos que retornam blocos ou recebem outro blocos como argumentos. Considerem o seguinte exemplo:
void (^(^complexBlock)(void (^)(void))(void) = ^(void (^aBlock) (void)) {
return ^{
};
};
A variável complexBlock se refere a um bloco que recebe outro bloco como argumento (aBlock) e retorna um outro bloco.
Reescrevendo o código para usar uma definição de tipo torna isso muito mais legível:
XYZSimpleBlock (^betterBlock) (XYZSimpleBlock) = ^(XYZSimpleBlock aBlock) {
return ^{
};
};


Objetos Usam Propriedades Para Acompanhar Blocos

A sintaxe para definir uma propriedade para o acompanhamento de um bloco é parecido para um bloco de variável:
@interface XYZObject : NSObject
@property (copy) void (^blockProperty) (void);
@end
Nota: Você deve especificar copy como o atributo de propriedade, porque um bloco precisa ser copiado manter o controle de seu estado capturado fora do escopo original.
Isso não é algo que você precisa se ​​preocupar quando usandoestiver usando a contagem de referência automática (ARC), como isso vai acontecer automaticamente, mas é a melhor prática, para a propriedade de atributo para mostrar o comportamento resultante. Para mais informações, consulte Tópicos de Programação com Blocos.
Uma propriedade bloco é definida ou invocada como qualquer outra variável de bloco:
self.blockProperty = ^ {
};
self.blockProperty();
Também é possível usar as definições de tipo de declaração de propriedade do bloco, como este:
typedef void (^XYZSimpleBlock) (void);
@interface XYZObject : NSObject
@property (copy) XYZSimpleBlock blockProperty;
@end


Anulando Ciclos de Referencia Forte Quando Capturar Self

 

Se você precisa capturar self em um bloco, tal como quando definimos um retorno de block, é importante considerar as implicações da gerencia de memória.
Blocos mantem forte referência para qualquer objecto capturado, incluindo self, isso significa que é fácil acabar com um forte ciclo de referência, se, por exemplo, um objeto mantém uma propriedade copy para um bloco que captura self:
 
@interface XYZBlockKeeper : NSObject
@property (copy) void (^block) (void);
@end
————————————–
@implementation XYZBlockKeeper
-(void) configureBlock {
self.block = ^{
[self doSomething]; // Captura uma referencia forte para self
//Cria um ciclo de referencia forte.
   };
}
@end
O compilador irar alertar você com um simples exemplo como esse, mas um mais um exemplo mais complexo poderá envolver multiplas fortes referencias entre os objetos para criar o ciclo, tornando mais difícil de diagnostivar.
Para anular este problema, a melhor prática é capturar fracas referencias para self, como abaixo:
-(void)configureBlock{
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; //Captura uma fraca referencia
// Para anular o ciclo de referência
   }
}
Ao capturar um ponteiro fraco para self, o bloco não vai manter um forte relacionamento de volta ao objeto XYZBlockKeeper.Se esse objeto é desalocado antes que o bloco é chamado, o ponteiro weakSelf simplesmente será ajustado para nil.

Blocos Podem Simplificar a Enumeração

 

Além de manipuladores gerais preenchimento, muitas API Cocoa e Cocoa Touch usam blocos para simplificar as tarefas comuns, como a enumeração de coleção. A classe NSArray, por exemplo, oferece três métodos baseados em blocos, incluindo:
-(void) enumerateObjectsUsingBlock:(void (^) (id obj, NSUInteger idx, BOOL *stop))block;
Este método recebe um argumento, que é um bloco para ser invocado quando cada item estiver no array:
NSArray *array = …
[array enumerateObjectUsingBlock: ^ (id obj, NSUInteger idx, BOOL *stop) {
NSLog(@”Object at index %lu is %@, idx, obj);
}];
O bloco em si leva três argumentos, os dois primeiros, que se referem ao objeto atual e seu índice no array. O terceiro argumento é um ponteiro para uma variável Boleana que você pode usar para parar a enumeração, como abaixo:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  if(…) {
    *stop = YES;
  }
)];
Também é possível customizar a enumeração através do uso do método enumerateObjectsWithOptions:usingBlock:. Especificando a opção NSEnumerationReverse, por exemplo, irá percorrer a coleção na ordem inversa.
Se o código no bloco de enumeração é do processador-interno — é segura para a execução simultânea — você pode usar a opção NSEnumerationConcurrent:
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
….
}];
Esse sinalizador indica que as invocações ao bloco de enumeração podem ser distribuídos em vários segmentos, oferecendo um aumento de desempenho potencial, se o código de bloco é particularmente processador. Note que a ordem de enumeração é indefinida ao usar esta opção.
A classe NSDictionary tambem oferece métodos baseados em blocos, incluindo:
NSDictionary *dictionary = …
[dictonaty enumerateKeysAndObjectsUsingBlock: ^ (id key, id obj, BOOL *stop) {
NSLog(@”key:%@, value: @”,key,obj);
}];
Isso torna mais conveniente para enumerar cada par chave-valor do que ao usar um loop tradicional, por exemplo.

Blocos Podem Simplificar Tarefas Simultâneas

 

Um bloco representa uma unidade distinta de trabalho, combinando código executável com estado opcional capturado do escopo envolvido. Isso o torna ideal para a invocação assíncrona utilizando uma das opções de simultaneidade disponíveis para OS X e iOS. Ao invés de ter que descobrir como trabalhar com mecanismos de baixo nível, como threads, você pode simplesmente definir suas tarefas usando os blocos e, em seguida, deixar que o sistema execute essas tarefas conforme os recursos do processador se tornam disponíveis.
OS X e iOS oferecem uma variedade de tecnologias para a concorrência, incluindo dois mecanismos de agendamento de tarefas: filas de Operação e Grand Central Dispatch. Estes mecanismos giram em torno da idéia de uma fila de tarefas à espera de ser chamado. Você pode adicionar seus blocos em uma fila na ordem que você precisa deles para ser invocado, eo sistema desenfileira-os para invocação quando o tempo e os recursos do processador se tornam disponíveis.
Uma fila em série somente permite que uma tarefa seja executada de cada vez – a próxima tarefa na fila não será retirada da fila e invocada enquanto a tarefa já iniciada não for finalizada. A fila concorrente invoca tantas tarefas quanto puder, sem esperar por tarefas anteriores terminem.

Use Operações De Bloco Com Filas De Operações

 

Uma operação em fila é em Cocoa e Cocoa Touch parecido com tarefas agendadas. Você cria uma instancia de NSOperation para encapsular uma unidade e trabalho juntamente com todos os dados nescessários, em seguida, adicione essa operação a uma NSOperationQueue para execução.
Embora você possa criar sua própria subclasse NSOperation personalizada para implementar tarefas complexas, também é possível usar o NSBlockOperation para criar uma operação usando um bloco, assim:
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^ {
}];
É possível executar uma operação manualmente, mas as operações são normalmente adicionados tanto por uma fila de operação existente ou uma fila que você mesmo criou, pronta para a execução:
// agendando tarefas na fila principal:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation: operation];
//agendando tarefas na fila de segundo plano:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
Se você usar uma fila de operação, você pode configurar as prioridades ou dependências entre as operações, como especificar que uma operação não deve ser executado até que um grupo de outras operações tenham sido completadas. Você também pode monitorar alterações para no estado de suas operações através de valores-chave de observação, o que faz com que seja fácil de atualizar um indicador de progresso, por exemplo, quando uma tarefa é concluída.
Para mais informações em operações e filas de operações, veja “Filas de Operações”.


Agende blocos em filas de Dispatch Com o Grand Central Dispatch

 

Se você precisa agendar um bloco arbitrário de código para a execução, você pode trabalhar diretamente com dispatch de filas controlada pelo Grand Central Dispatch (GCD).v Filas de despacho tornam fácil de executar tarefas de forma síncrona ou assíncrona em relação ao chamador, e executar suas tarefas em uma ordem first-in, first-out.
Você pode criar sua própria fila de dispatch ou usar uma das filas fornecidos automaticamente pelo GCD. Se você precisar agendar uma tarefa para a execução simultânea, por exemplo, você pode obter uma referência a uma fila existente usando a função dispatch_get_global_queue () e especificar uma prioridade da fila, como este:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
Para despachar o bloco para a fila, você usará a função dispatch_async() ou dispatch_sync(). A função dispatch_async() retorna imediatamente, sem esperar o bloco para ser invocado:
dispatch_async(queue, ^{
NSLog(@”Block for asynchronous execution”);
});
A função dispatch_sync() não retorna enquando o bloco não tenha completado a sua execução; você poderá usar isso em situações onde um bloco simultaneo precisa esperar por outra tarefa ser terminada no thread principal, antes de continuar, por exemplo.


Para mais informações em filas de dispatch e GCD, veja “Filas de Dispatch”.

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