Design Pattern para iOS – Parte 4

Esta é a quarta e última parte da tradução sobre padrões de desenvolvimento para iOS (Design Pattern).
Esta tradução foi feita a partir da página: http://www.raywenderlich.com/46988/ios-design-patterns, e pode conter erros. Por favor, informe se você viu algum erro para que possamos corrigi-lo.


Para quem ainda não viu segue link para as partes 1, 2 e 3 do tutorial

Design Pattern para iOS – Parte 1
Design Pattern para iOS – Parte 2
Design Pattern para iOS – Parte 3

Archiving

Uma das implementações especializadas da Apple do padrão Memento é o Arquivamento. Isso converte um objeto em um fluxo que pode ser salvo e depois restaurado sem expor propriedades privadas para classes externas. Você pode ler mais sobre esta funcionalidade no Capítulo 16 do iOS 6 em Tutoriais livro. Ou em Apple’s Archives and Serializations Programming Guide.

Como Usar Archiving

Primeiro, você precisa declarar que Album pode ser arquivado, conformando-se o protocolo NSCoding. Abra Album.h e altere a linha @interface da seguinte forma:

@interface Album : NSObject <NSCoding>

Adicione os dois métodos para Album.m seguintes:

– (void)encodeWithCoder:(NSCoder *)aCoder{

[aCoder encodeObject:self.year forKey:@”year”];
[aCoder encodeObject:self.title forKey:@”album”];
[aCoder encodeObject:self.artist forKey:@”artist”];
[aCoder encodeObject:self.coverUrl forKey:@”cover_url”];
[aCoder encodeObject:self.genre forKey:@”genre”];
}
[/code]

- (id)initWithCoder:(<NSCoder *)aDecoder{
   self = [super init];
   if (self){
             _year = [aDecoder decodeObjectForKey:@"year"];
             _title = [aDecoder decodeObjectForKey:@"album"];
             _artist = [aDecoder decodeObjectForKey:@"artist"];
             _coverUrl = [aDecoder decodeObjectForKey:@"cover_url"];
             _genre = [aDecoder decodeObjectForKey:@"genre"];
   }
 return self;
}

Você chama encodeWithCoder: quando se arquiva uma instância dessa classe. Por outro lado, você chama initWithCoder: quando desarquiva uma instância para criar uma instância de Album. É simples, mas poderosa.

Agora que a classe Album pode ser arquivada, adicione o código que realmente salva e carrega a lista de álbuns.

Adicione o seguinte assinatura (ou método protótipo) para PersistencyManager.h:

- (void)saveAlbums;

Este será o método que é chamado para salvar os álbuns.

Agora, adicione a implementação do método de PersistencyManager.m:


- (void)saveAlbums{
NSString *filename = [NSHomeDirectory() stringByAppendingString:@"/Documents/albums.bin"];
NSData*data = [NSKeyedArchiver archivedDataWithRootObject:albums];
[data writeToFile:filename atomically:YES];
}

NSKeyedArchiver arquiva o array de álbuns em um arquivo chamado albums.bin.

Quando você arquiva um objeto que contém outros objetos, o compactador automaticamente tenta recursivamente arquivar os objetos filho e quaisquer objetos filho das crianças, e assim por diante. Neste caso, o arquivamento começa com

albums, que é uma matriz de instâncias de álbum. NSArray e o Album suporta a interfaceNSCopying , tudo na matriz é automaticamente arquivado.

Agora substitua o init no PersistencyManager.m com o seguinte código:

- (id)init
{
self = [super init];
          if (self) {
              NSData *data = [NSData dataWithContentsOfFile:[NSHomeDirectory() stringByAppendingString:@"/Documents/albums.bin"]];
              albums = [NSKeyedUnarchiver unarchiveObjectWithData:data];

              if (albums == nil){
                  albums = [NSMutableArray arrayWithArray:
@[[[Album alloc] initWithTitle:@"Best of Bowie" artist:@"David Bowie" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:@"1992"],
[[Album alloc] initWithTitle:@"It's My Life" artist:@"No Doubt" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:@"2003"],
[[Album alloc] initWithTitle:@"Nothing Like The Sun" artist:@"Sting" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:@"1999"],
[[Album alloc] initWithTitle:@"Staring at the Sun" artist:@"U2" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:@"2000"],
[[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]];
[self saveAlbums];
            }
       }
  return self;
}

No novo código, NSKeyedUnarchiver carrega os dados do álbum do arquivo, se ele existir. Se ele não existir, ele cria os dados do álbum e salva-o imediatamente para a próxima execução do app.

Você também vai querer salvar os dados do álbum toda vez que o app vai para segundo plano. Isto pode não parecer necessário agora, mas e se você depois adicionar a opção de alterar dados do álbum? Então você iria querer isso para garantir que todas as suas alterações sejam salvas.

Adicione a seguinte assinatura de método para LibraryAPI.h:

- (void)saveAlbums;

Desde que o aplicativo principal acessa todos os serviços através de LibraryAPI, isto é como o aplicativo permitirá PersitencyManager sei que ele precisa salvar os dados do álbum.

Agora adicione a implementação do método de LibraryAPI.m:

- (void)saveAlbums
{
[persistencyManager saveAlbums];
}

Este código simplesmente passa em uma chamada a LibraryAPI para salvar os álbuns para PersistencyMangaer.

Adicione o seguinte código ao final da saveCurrentState em ViewController.m:

[[LibraryAPI sharedInstance] saveAlbums];

E o código acima usa LibraryAPI para ativar a gravação de dados sempre que o álbum ViewController salva seu estado.

Build seu app para verificar se tudo compila.

Infelizmente, não há nenhuma maneira fácil de verificar se a persistência de dados está correta embora. Você pode verificar a pasta Documentos do simulador do seu aplicativo no Finder para ver que o arquivo de dados do álbum é criada, mas para ver quaisquer outras alterações você teria que adicionar na capacidade de alterar os dados do álbum.

Mas em vez de alterar os dados, e se você adicionar uma opção para apagar os álbuns que você não quer mais em sua biblioteca? Além disso, não seria bom ter uma opção de desfazer se você excluir um álbum por engano?

Isto oferece uma grande oportunidade para falar sobre o último padrão da lista: Command.

Command Pattern

O padrão de projeto Command encapsula uma solicitação ou ação como um objeto. O pedido encapsulado é muito mais flexível do que um pedido cru e pode ser transmitido entre objetos, guardados para mais tarde, modificado de forma dinâmica, ou colocadas em uma fila. A Apple implementou esse padrão usando o mecanismo Target-Action (alvo para a ação) e Invocação.

Você pode ler mais sobre Target-Action na documentação da Apple, mas Invocation usa a classe NSInvocation que contém um objeto alvo, um selector de método e de alguns parâmetros. Este objeto pode ser alterado dinamicamente e executado quando necessário. É um exemplo perfeito do padrão Command em ação. Ele separa o objeto do envio do objeto receptor ou objetos e pode persistir um pedido ou uma cadeia de pedidos.

Como usar Command Pattern

Antes de entrar na invocação de ações, você precisa definir o framework para desfazer ações. Portanto, você deve definir o UIToolbar eo NSMutableArray necessário para a pilha de desfazer.

Adicione o seguinte código para a extensão de classe em ViewController.m onde você definiu todas as outras variáveis​​:

UIToolbar *toolbar;
// Usaremos essa matriz como uma pilha de operações para push e pop, para a opção undo(desfazer)
NSMutableArray *undoStack;

Isso cria uma barra de ferramentas que irá mostrar os botões para a nova ação, , bem como uma matriz para agir como a fila de comando.

Adicione o seguinte código para o início do viewDidLoad: (à direita antes de comentário # 2):

toolbar = [[UIToolbar alloc] init];
UIBarButtonItem *undoItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemUndo target:self action:@selector(undoAction)];
undoItem.enabled = NO;
UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *delete = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteAlbum)];
[toolbar setItems:@[undoItem,space,delete]];
[self.view addSubview:toolbar];
undoStack = [[NSMutableArray alloc] init];

O código acima cria uma barra de ferramentas com dois botões e um espaço flexível entre eles. Ele também cria uma pilha de desfazer vazia. O botão de desfazer está desativado aqui, porque a pilha de desfazer começa vazia.

Além disso, observe que a barra de ferramentas não será iniciada com um frame, como o tamanho do frame definido em viewDidLoad não é definitiva. Portanto, definir o frame final através do seguinte bloco de código uma vez que o frame da view é finalizada pela adição do código para ViewController.m:

- (void)viewWillLayoutSubviews
{
toolbar.frame = CGRectMake(0, self.view.frame.size.height-44, self.view.frame.size.width, 44);
dataTable.frame = CGRectMake(0, 130, self.view.frame.size.width, self.view.frame.size.height - 200);
}

Você vai adicionar três método para ViewController.m para lidar com ações de gestão álbum: adicionar, excluir e desfazer.

O primeiro método é para adicionar um novo album:

- (void)addAlbum:(Album*)album atIndex:(int)index
{
[[LibraryAPI sharedInstance] addAlbum:album atIndex:index];
currentAlbumIndex = index;
[self reloadScroller];
}

Aqui você adiciona o álbum, defini-lo como o índice do atual álbum, e recarrega o scroller.

Em seguida, vem o método de exclusão:

- (void)deleteAlbum
{
// 1
Album *deletedAlbum = allAlbums[currentAlbumIndex];

// 2
NSMethodSignature *sig = [self methodSignatureForSelector:@selector(addAlbum:atIndex:)];
NSInvocation*undoAction = [NSInvocation invocationWithMethodSignature:sig];
[undoAction setTarget:self];
[undoAction setSelector:@selector(addAlbum:atIndex:)];
[undoAction setArgument:&amp;deletedAlbum atIndex:2];
[undoAction setArgument:&amp;currentAlbumIndex atIndex:3];
[undoAction retainArguments];

// 3
[undoStack addObject:undoAction];

// 4
[[LibraryAPI sharedInstance] deleteAlbumAtIndex:currentAlbumIndex];
[self reloadScroller];

// 5
[toolbar.items[0] setEnabled:YES];
}

Existem algumas características novas e emocionantes neste código, assim que considerar cada seção comentados a seguir:

1 – Recebe o álbum a ser excluído.

2 – Define um objeto do tipo NSMethodSignature para criar o NSInvocation, que será usado para reverter a ação de exclusão se o usuário mais tarde decide desfazer a exclusão. O NSInvocation precisa saber três coisas: o selector (qual é a mensagem a enviar), o alvo (para quem enviar a mensagem) e os argumentos da mensagem. Neste exemplo, a mensagem enviada é o oposto de excluir desde quando você desfazer uma exclusão, é necessário adicionar novamente o álbum excluído.

3 – Após a undoAction tenha sido criada você deve adicioná-la à undoStack. Esta acção irá ser acrescentado ao final da matriz, assim como de uma pilha normal.

4 – Usa LibraryAPI para excluir o álbum a partir da estrutura de dados e recarrega o scroller.

5 – Desde que há uma ação na pilha de desfazer, você precisa habilitar o botão de desfazer.

Nota: Com NSInvocation, você precisa manter os seguintes pontos em mente:

  • Os argumentos devem ser passados ​​por ponteiro.
  • Os argumentos começam no índice 2; índices 0 e 1 são reservados para o alvo e o selector.
  • Se há uma chance de que os argumentos serão deallocated, então você deve chamar retainArguments.

Finalmente, adicione o método para a ação de desfazer:

- (void)undoAction{
   if (undoStack.count &gt; 0){
      NSInvocation *undoAction = [undoStack lastObject];
      [undoStack removeLastObject];
      [undoAction invoke];
   }

   if (undoStack.count == 0){
   [toolbar.items[0] setEnabled:NO];
   }
}

A operação de desfazer “pops”(remove) o último objeto na pilha. Este objeto é sempre do tipo NSInvocation e pode ser chamado por chamadas … invocações. Isso chama o comando que você criou anteriormente, quando o álbum foi excluído, e adiciona o álbum eliminado de volta para a lista de álbuns. Desde que você também deletou o último objeto na pilha quando você “popped”(removeu), você agora verifica para ver se a pilha está vazia. Se estiver, isso significa que não há mais ações para desfazer. Então você desativa o botão Undo.

Build e execute o aplicativo para testar o seu mecanismo de undo, excluir um álbum (ou dois) e aperte o botão Undo para vê-lo em ação:

4-1

Este também é um bom lugar para testar se as alterações aos seus dados álbum é retida entre as sessões. Agora, se você excluir um álbum, enviar o aplicativo para o fundo, em seguida, terminar a aplicação, a próxima vez que você iniciar o aplicativo da lista de álbuns exibido deve refletir a exclusão.

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