Swift - Opcionais e Coleções

oieteus
1,697 views

Open Source Your Knowledge, Become a Contributor

Technology knowledge has to be shared and made accessible for free. Join the movement.

Create Content

Introduzindo opcionais

Quando nós declaramos variáveis em Swift por padrão elas não são opcionais, isto significa que elas devem ter um valor válido, não podem ter um valor nil. Se nós tentarmos atribuir nil para uma variável não opcional, isto vai nos gerar um erro.

Por exemplo, o código abaixo lançará um erro quando tentarmos atribuir nil para a variável message porque ela é de um tipo não opcional.

Não deixe passar

É muito importante entender que nil em Swift é muito diferente de nil em Objective-C ou outras linguagens baseadas em C. Nessas linguagens, nil é um ponteiro para um objeto inexistente; no entanto, em Swift, um valor nil é a ausência de um valor. Entender esse conceito é muito importante para entender completamente os opcionais em Swift.

Uma variável declarada como opcional pode conter um valor válido, ou pode indicar a ausência de valor. Nós indicamos ausência de valor atribuindo nil para nossas variáveis.

O código abaixo mostra como podemos definir uma variável String opcional:

O sinal de interrogação indica que a variável myName é opcional. Nós lemos esta linha de código dizendo que a variável myName é do tipo Optional, que pode conter um valor do tipo String ou pode não conter nada nil.

Observe as duas linhas código abaixo, eles representam a mesma coisa:

As duas linhas de código acima são equivalentes. Em Swift o tipo Optional é uma enumeração com dois valores possíveis, None e Some(T), onde T é um valor genérico associado. Não se preocupem, nós iremos discutir esta parte de Generics em materiais futuros. Internamente o tipo opcional é definido da seguinte forma:

enum Optional<T> {
    case Nome
    case Some(T)
}

A necessidade de opcionais em Swift

Pense nos booleanos: eles podem ser verdadeiros ou falsos. Isso significa que um Bool opcional pode ser verdadeiro, falso ou nenhum dos dois – pode ser nil. Isso é um pouco difícil de entender mentalmente.

Bem, me responda: eu gosto de cerveja? A menos que você seja um amigo meu ou talvez me siga de perto no Instagram, você não pode dizer com certeza – você definitivamente não pode dizer Verdadeiro (eu gosto de cerveja) ou Falso (eu não gosto de cerveja), porque você realmente não sabe. Claro, você poderia me perguntar e descobrir, mas até que você faça isso a única resposta segura é “não sei”, que neste caso poderia ser representada tornando o booleano um opcional com valor nil.

Clique aqui para ver um artigo mais completo sobre o assunto.

Usando opcionais

A chave para usar opcionais é sempre verificar se eles contêm um valor válido antes de acessá-los. A razão para isso é que, se tentarmos usar um valor opcional sem verificar se ele contém um valor válido, podemos encontrar um erro em tempo de execução, fazendo com que nosso aplicativo falhe. Usamos o termo unwrapping para nos referirmos ao processo de recuperar um valor de um opcional. Vamos introduzir dois métodos para recuperar os valores de um opcional.

Forced unwrapping de um opcional

Para recuperar o valor de um opcional nós podemos usar um ponto de exclamação ! depois do nome da variável. Isto é chamado de forced unwrapping, porém esta maneira é muito perigosa e deve ser usada somente se tivermos certeza que a variável contém um valor e não nil. Por outro lado se a variável estiver com valor igual a nil, nós iremos obter um erro na aplicação em tempo de execução e isso fará o aplicativo encerrar instanteamente.

Quando nós usamos o ponto de exclamação para recuperar um valor diferente de nil de um opcional estamos falando para o compilador não se preocupar porque temos a certeza que naquela variável tem um valor válido, ou seja, alguma coisa diferente de nil.

Neste código acima se você deixar da seguinte forma: myString1 = nil, nós iremos tomar um erro de execução, antes de executar este tipo de código o compilador sequer nos informa que irá dar problema, pois como eu falei: quando usamos o ponto de exclamação estamos informando ao compilador para ele ficar na dele, pois nós sabemos o que estamos fazendo. Aqui entre nós, não é uma boa prática fazer isso.

Nós sempre devemos verificar se uma variável tem um valor válido antes de desumbrulhar ela. O exemplo abaixo mostra um caminho para isso:

Agora, se a linha que define o opcional myString1 para o valor teste fosse removida, não receberíamos um erro em tempo de execução porque apenas desempacotamos o opcional myString1 se ele contiver um valor válido (não nulo). Desempacotar opcionais, como acabamos de mostrar no código acima não é ideal, e não é recomendado que opcionais sejam desempacotados dessa maneira. Podemos combinar a verificação e o desempacotamento em uma única etapa, chamada de Optional binding.

Optional Binding

Optional Binding é o caminho recomendado para verificar e desempacotar opcionais.

Observer o código abaixo ele utiliza a estrutura if let:

Além do if let existe outro optional binding recomendado, que é o guard let.

Pesquise por conta própria

  • Qual a diferença entre if let e guard let?

Operador de coalescência nula

O operador de coalescência nula tenta desempacotar um opcional e, se contiver um valor, retornará esse valor ou um valor padrão se o opcional for nil, conforme mostrado no código a seguir.

optionalA ?? defaultValue

No exmplo abaixo, vamos demonstrar o operador de coalescência nula onde um dos opcionais contem nil e o outro contém um valor válido.

O operador de coalescência nula é uma forma mais limpa de escrever algo como o código abaixo:

var nameC = optionalA != nil ? optionalA! : defaultName

Usando coleções

Uma coleção agrupa múltiplos itens dentro de um item único. Swift possui três tipos nativos de coleção: Arrays, Dictionaries e Sets.

  • Arrays aramazenam os itens de forma ordenada.
  • Dicionários é uma coleção não ordenada, e utiliza o padrão chave-valor.
  • Sets são coleções não ordenadas e só podem conter valores únicos.

Em um array, acessamos os dados pela localização ou índice dentro do array, enquanto que em um set geralmente iteramos pela coleção e os dicionários são acessados ​​usando uma chave exclusiva.

O tipo do dado das coleções do Swift tem que ser o mesmo. Isto significa, por exemplo, que não podemos armazenar uma String em um array de Int.

Arrays

Arrays existem em todas as linguagens de programação da atualidade. Em Swift, arrays é uma lista ordenada de objetos do mesmo tipo. Quando um array é criado, nós devemos declarar o tipo do dado que queremos armazenar usando uma declaração explícita de tipo ou através da inferência do tipo.

Nós somente utilizamos a declaração explícita do tipo de um array quando criamos um array vazio. Se nós inicializarmos um array com dados, o compilador uso a inferência de tipo para inferir o tipo de dados para o array.

Criando e incializando arrays

O código abaixo mostra como podemos definir um array imutável de inteiros usando a palavra chave let

Se você quiser criar um array mutável, nós podemos usar a palavra chave var na hora de definir o array.

O exemplo a abaixo mostra como definir um array mutável:

Nos dois exemplos anteriores, o compilador inferiu o tipo dos dados que queríamos armazenar nos arrays. Se quisermos criar um array vazio, nós precisamos explicitar o tipo do dado que queremos armazenar. Abaixo segue dois caminhos diferentes para criar arrays mutáveis e vazios em Swift:

Nos exemplos anteriores criamos somente arrays do tipo Int, mas você pode criar arrays de qualquer tipo, inclusive de um tipo personalizado que vocês podem criar usando class, struct, enum.

Arrays unidimensionais são bem mais comuns no dia a dia profissional, mas arrays multidimensionais também podem ser declarados em Swift. O array multidimensional é realmente nada mais do que um array de arrays.

O seguinte exemplo mostra dois caminhos para criar dois arrays multidimensionais:

Acessando o elemento de um array

A sintaxe de subscript é usada para recuperar valores de um array, ela é basicamento o número aparecendo dentro dos colchetes antes do indentificador do array, este número especifica a localização (índice) do elemento do array que você deseja recuperar.

Um fato importante a ser observado é que os índices em arrays Swift começam com o número zero. Isso significa que o primeiro item em um array tem um índice de 0. O segundo item em um array tem um índice de 1.

first and last

Nós podemos recuperar o primeiro e o último elemento de um array usando as propriedades: first e last. As propriedades first e last retornam um valor opcional, dado que o valor pode ser nil para ambas propriedades, se o array for vazio.

O seguinte exemplo mostra como usar estas propriedades para recuperar o primeiro e o último elementeo de um array unidimensional.

Contando os elementos de um array

É essencial conhecer a quantidade de elementos de um array. O tipo Array em Swift contem uma propriedade somente de leitura, chamada count. O seguinte exemplo mostra como podemos usar esta propriedade para recuperar a quantidade de elementos de um array:

Se você tentar recuperar o elemento de um array, onde o índice está fora do intervalo esperado você receberá o seguinte erro: array index out of range

Pesquise por conta própria

  • Como podemos verificar se um array está vazio em Swift?
  • Como embaralhar um array usando Swift?
  • Como inserir um elemento em um indíce específico do array?
  • Como substituir um determinado elemento em um array dado um indice específico?

Removendo elementos do array

Aqui vai três métodos que nós podemos usar para remover um ou todos os elementos de um array. Estes métodos são: reomveLast(), remove(at:) e removeAll()

O seguinte exemplo mostra como usar os três métodos para remover os elementos de um array:

Os métodos removeLast() e remove(at:) retornam o valor removido do array. Caso você queria saber qual o elemento removido basta atribuir a saída da função para uma constante ou variável.

Mesclando dois arrays

Para criar um novo array adicionando dois outros arrays nós utilizamos o operador de adição +. O seguinte código mostra como usar o operador de adição para criar um novo array com todos os elementos dos outros dois arrays:

Pesquise por conta própria

  • Como funciona os métodos sort() e sorted() dos arrays?

Filter

O método filter retorna um novo array que foi filtrado a apartir do original. Este é um dos métodos mais poderosos, se você precisa recuperar um subconjunto do array baseado em um conjunto de regras próprio o filter é um forte aliado. O fechamento(closure) pega um argumento, e retorna um booleano com o valor true se o valor deve ser inserido no novo array, como mostra o seguinte código:

Map

Enquanto filter é usado para selecionar somente certa parte do array, map é usado para aplicar uma determinada lógica em todos os elementos de um array. O seguinte exemplo mostra como usar o método map para dividir todos os números de um do tipo Int array por 10:

O novo array criado não precisa necessariamento ter o mesmo tipo do array mapeado, contudo todos os elementos do novo array deve ser do mesmo tipo. No seguinte exemplo o array original contem valores do tipo Int e o novo array criado contém valores do tipo String:

ForEach

Nós podemos utilizar o forEach para iterar sobre uma sequência. O seguinte exemplo mostra como podemos fazer isso:

Iterando através de um array

Nós podemos iterar sobre todos os elementos de um array, em ordem, com um loop for-in. O loop for-in execurá uma ou mais declarações para cada elemento do array. Nós discutiremos o loop for-in com mais detalhes em playgrounds futuros. O código abaixo mostra como podemos usar o for-in para iterar sobre os elementos de um array:

Algumas vezes queremos iterar sobre os elementos de um array e além de conhecer o valor queremos saber qual o índice também, para isso podemos usar o método enumerated(), que retorna uma tupla para cada item do array contendo o índice e o valor de cada elemento. O exemplo mostra como utilizar esta função:

Dicionários

Enquanto dicionários não são comumente utilizados como arrays eles tem funcionalidades adicionais que fazem eles incrivelmente poderosos. Um dicionário é um recipiente que adiciona múltiplos pares chave-valor, onde todas as chaves são do mesmo tipo e todos os valores também são do mesmo tipo. As chaves são usadas como identificadoras únicas para um determinado valor. Os dicionários não garantem a ordem em que os elementos são armazenados. Países e suas siglas são um ótimo exemplo de coisas que podem ser armazenadas usando dicionários.

Criando e inicializando dicionários

Nós podemos inicializar um dicionários para armazenar países e suas siglas da seguinte forma:

O código anterior cria um dicionário imutável dado que estamos utilizando a palavra chave let. Caso queiram criar um dicionário mutável basta declarar o dicionário utilizando a palavra chave var.

Nos exemplos anteriores, nós criamos dois dicionários onde as chaves e os valores eram do tipo String. O compilador infere o tipo das chaves e valores por conta do tipo das chaves e valores que foram usadas na inicialização. Se nós quisermos criar um dicionário vazio, nós precisamos dizer ao compilador qual o tipo das chaves e dos valores.

Se quisermos utilizar um objeto customizado como chave em nossos dicionários, nós prescisamos fazer o nosso objeto customizado conformar com o protocolo Hashable. Nós iremos discutir amplamente sobre protocolos em outros playgrounds. Por agora o importante é você saber que se caso quiser você pode utilizar um objeto customizado como chave do seu dicionário.

Acessando valores de um dicionário

Nós usamos uma sintaxe de subscript para recuperar o valor de uma chave particular. Se no dicionário não existir a chave solicitada o valor retornado será nil, portanto a variável retornada é opcional. O seguinte exemplo mostra como recuperar o valor de um dicionário utilizando a sintaxe de subscript:

Pesquise por conta própria

  • Como contar as chaves ou valores de um dicionário?
  • Como podemos validar se um dicionário é vazio ou não?
  • Como atualizar o valor para uma determinada chave em um dicionário?
  • Como remover o par chave-valor de um dicionário?

Sets

O tipo de Set é uma coleção genérica semelhante ao tipo Array. Enquanto o tipo Array é uma coleção ordenada que pode conter itens duplicados, o tipo Set é uma coleção não ordenada onde cada item deve ser único.

Inicializando um Set

Com uma inicialização bem parecida com a de um array ou dicionário. Se quisermos criar um Set vazio devemos informar qual o tipo do dado que será armazenado, por outro lado se já atribuirmos um determinado tipo de dado para o nosso Set Swift utilizará a inferência de tipo para criá-lo corretamente. Para criar um Set mutável usamos a palavra chave var se quisermos criar um Set imutável utilizamos a palavra chave let.

Inserindo itens em um Set

O exemplo abaixo mostra como podemos usar o método insert() para inserirmos elementos em nosso Set:

O método insert retorna uma tupla, nós podemos usá-la para verificar se um determinado valor foi realmente adicionado ao nosso Set. O exemplo abaixo mostra como podemos usar o retorno do método para validar se o valor foi inserido com sucesso:

Neste exemplo aparecerá no console Failed, pois a palavra One já foi adicionada previamente no Set por isso ela não pode ser adicionada novamente, dada que um Set não pode possuir valores duplicados.

Pesquise por conta própria

  • Como podemos verificar se um determinado item existe em um Set?
  • Como seria um código que itera sobre todos os itens de um Set?
  • Como podemos remover um item de um set? E como podemos remover todos os itens de um set?
Open Source Your Knowledge: become a Contributor and help others learn. Create New Content