Quaddro + MacMagazine: Swift na prática #8 — Optionals e Type Casting

por Leandro Cissoto

Estamos chegando hoje ao nosso oitavo artigo sobre Swift na parceria Quaddro + MacMagazine, para falarmos de Optionals e Type Casting. Vamos entender melhor o porquê de vermos um monte de exclamações e interrogações por aí quando vemos algum código em Swift.

Ícone da Swift

Lembramos que, para testar os conceitos aqui apresentados, recomendamos o uso do Xcode 6 ou superior. Se você não possui um Mac ou não quer instalar o Xcode, é possível utilizar uma ferramenta online que permite escrever em Swift diretamente pelo navegador.


Ícone do app Xcode

Xcode

de Apple

Compatível com Macs
Versão 10.3 (6.1 GB)
Requer o macOS 10.14.3 ou superior

Grátis

Badge - Baixar na Mac App Store

Código QR Código QR

Teoria

Não existe, no mundo da programação, uma forma universal de tratarmos a ausência de valores. Essa falta de padrão pode ser um problema. Até podemos utilizar ferramentas como NSNotFound, NSIntegerMax, entre outras para representar a ausência de valores nulos, mas não resolve o problema do padrão, e é exatamente aí que entram os Optionals.

Os Optionals foram inseridos na Swift justamente para unificar a representação de vazio e flexibilizar os tipos. Ao utilizá-los, podemos até perder um pouco da facilidade do uso do nil — quem vem do Objective-C sabe bem do que estou falando — mas ganhamos performance, segurança e uma forma consistente de lidarmos com nulos, independente do tipo de variável.

Entendendo os Optionals

Vamos fazer uma declaração de uma variável utilizando um Optional:

var meuNome: String?

Perceba que, ao declararmos o tipo da variável, utilizamos ?. Isto quer dizer que a variável meuNome é um Optional.

O fato de a variável ser um Optional não significa que ela necessariamente precisa ser do tipo String. A leitura correta seria “a variável meuNome é um Optional, que pode ou não ser do tipo String”. Essa informação é muito importante pois significa que, mesmo com a declaração do tipo, não significa que você está lidando especificamente com o tipo especificado.

O fato de a variável meuNome ser um Optional também faz com que ela possa receber um valor nulo.

meuNome = nil

Desempacotando valores

Quando declaramos uma variável ou constante como Optional, estamos dizendo que ela precisa ser desempacotada para ser utilizada. Podemos fazer isso de basicamente três formas:

  1. Utilizando o Unwrap através do operador !
  2. Inserindo o valor em uma constante temporária, usando o Optional Binding
  3. Ou podemos colocar um valor padrão, caso a variável seja nula.

USANDO O FORCE UNWRAP

O Force Unwrap é utilizado quando desejamos desempacotar um Optional o qual temos certeza que tem um valor. Isso significa que, se tentarmos desempacotar um valor nulo usando o Force Unwrap, a aplicação retornará um erro. Então, muito cuidado com esse carinha aqui.

var numero : Int? = 10

println(numero)
//Resultado: "Optional(10)"

println(numero!)
//Resultado: “10”

Perceba que, se não fizermos o desempacotamento, ao exibirmos a informação na tela utilizando println é exibido “Optional(10)”. Isso significa que o valor de número precisa ser desempacotado.

Ao utilizarmos o Force Unwrap forçamos o desempacotamento da informação, e a aplicação funciona da maneira correta. Essa abordagem exige muita atenção, pois o Force Unwrap não consegue desempacotar valores nulos — até porque, não há o que desempacotar.

OPTIONAL BINDING

Esta é forma mais segura de fazermos o desempacotamento. Através dela podemos verificar se haverá falha ao desempacotar e, com isso, tratarmos a ausência de valor. A melhor maneira de fazer isso é utilizando um if que irá atribuir o possível valor da variável a uma constante. Se tudo estiver correto, vai entrar no bloco.

Exemplo:

var meuFloat: Float?

meuFloat = 10.5

if let num = meuFloat {
    println("O valor é \(num)")
}else{
    println("O valor é vazio")
}

//Resultado: “O valor é 10.5”

O resumo da ópera é: o if tenta inserir na constante num o valor de meuFloat, e, caso haja valor, desempacota o valor e o atribui a constante num. Caso falhe, cairá no bloco do else.

Há também um jeito maroto de se fazer isso. Podemos eliminar o if colocando um valor padrão caso o desempacotamento falhe.

var passaro: String?

let meuPassaro = passaro ?? "Pombo do Nilo"

meuPassaro
//Resultado: “Pombo do Nilo”

Nota: fiquem atentos ao comando guard na Swift 2. Vai ser uma ferramenta poderosa para utilizar os Optionals.

Acredito que, com essas informações, já fica mais fácil entender aquele monte de interrogações e exclamações pelos códigos Swift que encontramos na vida. Mas se você pensa que já acabou, está enganado.

Type Casting

A utilização do Type Casting é a melhor maneira de descobrir a origem dos tipos. Com a utilização de “casting” poderemos detectar qual foi o tipo utilizado para instanciar um objeto. Esta é uma maneira muito eficaz na pesquisa da hierarquia das classes, que veremos em artigos futuros, ou para comparar tipos.

Iremos aprender como utilizar os operadores is e as, e entender como utilizá-los.

O OPERADOR IS

O operador is é o operador que checa o tipo de uma instância. A verificação com is retorna true no caso de a instância ser do mesmo tipo que o verificado, caso contrário retorna false.

Exemplo:

var seraArray: Array = ["Gervásio", "Lindoelma", "Mummy"]

seraArray is Array
//Resultado: true

seraArray is String
//Resultado: false

seraArray is Int
//Resultado: false

Vamos ver um pouco mais deste operador quando chegarmos em orientação a objeto, em que poderemos verificar se uma classe faz parte de uma determinada hierarquia.

Any e AnyObject

A linguagem Swift possui dois tipos de objetos que são utilizados para trabalhar com tipos indefinidos.

  • Any representará qualquer tipo.
  • AnyObject representará qualquer objeto.

Use Any e AnyObject somente quando você precisa explicitamente alterar o comportamento e as capacidades que eles fornecem. É sempre melhor ser específico sobre os tipos que você espera para trabalhar com o seu código.

Downcasting com o operador as

O operador as é utilizado para indicarmos um determinado tipo, caso este não esteja explicito. É muito utilizado especialmente quando temos um dado do tipo Any ou AnyObject.

Este operador pode ser utilizado de duas formas, sendo a primeira com o operador ! que irá forçar o “casting”, desempacotando o objeto, ou então como as? que neste caso irá tentar fazer o “casting” e retornará nulo caso não tenha sucesso.

Exemplo:

var numeroTexto: Dictionary<String, Any> = ["nome": "Leleco", "idade": 28, "profissao": "Programador", "peso": 89.6]

numeroTexto["nome"] as! String
numeroTexto["idade"] as! Int
numeroTexto["profissao"] as? Int

Criei um dicionário do tipo Any para receber valores de qualquer tipo e, abaixo, fiz o downcasting utilizando o perigoso Force Unwrap. Fiz isso porque tinha certeza do tipo ao chamar numoerTexto[“nome”] e numeroTexto[“idade”], que era do tipo Int. Quando chamei numeroTexto[“profissao”], testei o que acontece quando não temos certeza do tipo. Fiz um downcasting com chances de falha e que pode retornar nulo.

Exercício guiado

  1. Crie um novo documento chamado “Optionals” e deixe-o totalmente em branco.

Quaddro ensinando Swift

  1. Vamos criar agora um array de dicionários. Estes receberão uma String como chave, e um Any como valor. Fiz isso de maneira bem “verbosa” para exibir todos os detalhes possíveis da declaração, mas vocês podem resumir como quiserem. 🙂
var bancoDeDados: Array<Dictionary<String, Any>> = Array()

Quaddro ensinando Swift

  1. Agora que declaramos o nosso array, vamos inserir alguns valores dentro dele como se fosse um banco de dados. Essa estrutura é muito semelhante à de quando consumimos um web service.
bancoDeDados.append(["nome": "Leandro",
                     "peso": 89.6,
                     "profissao": "Programador",
                     "faltas": 36,
                     "ativo": true])

bancoDeDados.append(["nome": "Amanda",
                    "peso": 70.0,
                    "profissao": "Secretária",
                    "faltas": 10,
                    "ativo": "true"
])

bancoDeDados.append(["nome": "Gustavo",
                    "peso": 75.0,
                    "profissao": "Vendedor",
                    "faltas": 1475,
                    "ativo": false])

Quaddro ensinando Swift

  1. Agora que já temos o nosso array “abastecido”, vamos percorrê-lo utilizando for in.
for pessoa in bancoDeDados{

}

Quaddro ensinando Swift

  1. Vamos agora resgatar e fazer o downCasting das informações que estão no dicionário. Eu particularmente gosto de colocar os valores dentro de constantes para organizar e ter uma visão mais precisa do que está acontecendo.
for pessoa in bancoDeDados{

    
    let nome = pessoa["nome"] as! String
    let peso = pessoa["peso"] as? Double
    let profissao = pessoa["profissao"] as! String
    let ativo = pessoa["ativo"] as? Bool
        
}

Quaddro ensinando Swift

Perceba que utilizei em alguns casos o operador ! para já desempacotar diretamente. Nos casos em que usei o operador ?, o desempacotamento deve ser feito. Observe que, em ativo, coloquei um valor padrão false caso ele falhe ao desempacotar.

  1. Vamos verificar se a pessoa está ativada consultando a informação do dicionário. Usaremos um condicional e dentro deste bloco vamos fazer um print das informações, caso contrário, avisamos que o usuário foi desativado. O bloco for inteiro ficou assim:
for pessoa in bancoDeDados{
    
    
    let nome = pessoa["nome"] as! String
    let peso = pessoa["peso"] as? Double
    let profissao = pessoa["profissao"] as! String
    let ativo = (pessoa["ativo"] as? Bool) ?? false
    
    if ativo == true {
        println("O nome da pessoa é \(nome)")
        println("Ela pesa \(peso!) kg")
        println("Sua profissão é \(profissao)")
    }else{
        println("Este funcionário foi desativado")
    }
    println("----------------------------")   
}

Quaddro ensinando Swift

Tem uma pegadinha aí. Ele rodou duas vezes no false, mas se olharmos de volta lá no item 3 do nosso exercício guiado, verá que temos dois itens como true. Isto tem uma explicação: perceba que, na segunda inserção, colocamos por engano o valor true, que deveria ser um Bool, como String. Por sorte, fomos prevenidos ao colocar um valor padrão como false, caso o downcasting falhasse.

É por essas e outras que é muito importante dominar as tais interrogações e exclamações. 😉

·   ·   ·

Desta vez vou poupá-los de desafio, porque na semana que vem teremos dois — um inclusive envolvendo o conteúdo deste artigo.

Lembramos que no nosso GitHub você encontra os exercícios de todos os artigos, bem como as soluções dos desafios passados. No próximo artigo, falaremos de Enums e Structs. Até lá!

Posts relacionados

Comentários