O Básico

Swift é uma nova linguagem de programação para desenvolvimento de aplicativos iOS, macOS, watchOS e tvOS, no entanto, muitas partes do Swift serão familiares de sua experiência de desenvolvimento em C e Objective-C.

Ele fornece suas próprias versões de todos os tipos básicos da linguagem C e Objective-C, incluindo Int para inteiros, Double e Float para valores com casas decimais, Bool para valores booleanos e String para dados textuais. Ele também fornece versões poderosas dos três principais tipos de coleções, Array, Set e Dictionary, conforme descrito em sessões futuras.

Como na linguagem C, o Swift usa variáveis ​​para armazenar e referenciar a valores por um nome que identifique-os e também faz uso intenso de variáveis ​​cujos valores não podem ser alterados, estes são conhecidos como constantes e são muito mais poderosos do que constantes em C. Constantes são usadas em todo Swift para tornar o código mais seguro e mais claro na intenção quando você trabalha com valores que não precisam ser alterados.

Além de tipos comuns que todos conhecem, o Swift apresenta tipos avançados não encontrados no Objective-C, como as tuples(ou tuplas). As tuplas permitem que você crie e transmita agrupamentos de valores sendo possível usar uma tupla para retornar vários valores de uma função como um único valor composto.

Ele também apresenta valores opcionais, que lidam com a ausência de um valor. Os opcionais dizem que "há um valor, e é igual a x" ou "não existe um valor". O uso de opcionais é semelhante ao uso de nulo com ponteiros no Objective-C, mas eles funcionam para qualquer tipo, e não apenas as classes. Não só os opcionais são mais seguros e mais expressivos do que os ponteiros nulos no Objective-C, como também estão no coração de muitos dos recursos mais poderosos do Swift.

Swift é um linguagem type-safe (tipagem segura), o que significa que o idioma ajuda você a ser claro sobre os tipos de valores com os quais seu código pode funcionar. Se uma parte do seu código requer um String, o type-safe evita que você passe um Int por engano e da mesma forma, o type-safe impede que você acidentalmente passe uma cadeia de caracteres opcional para um código que requer uma cadeia não-opcional. O type-safe ajuda a capturar e corrigir erros o mais cedo possível no processo de desenvolvimento.

Constantes e Variáveis

Constantes e variáveis ​​associam um nome (como maxNum ou welcomeMessage) com um valor de um tipo específico (como o número 10 ou a String "Olá"). O valor de uma constante não pode ser alterado uma vez que for setado, enquanto uma variável pode ser setada para um valor diferente no futuro.

Declarando constantes e variáveis

Constantes e variáveis ​​devem ser declaradas antes de serem usadas, e você deve declarar constantes com o comando let e para as variáveis o comando var. Aqui está um exemplo de como as constantes e variáveis ​​podem ser usadas para rastrear o número de tentativas de login que um usuário fez:

Este código pode ser lido como: "Declare uma nova constante chamada maximumNumberOfLoginAttempts, e dê-lhe um valor de 10 e então declare uma nova variável chamada currentLoginAttempt e dê-lhe um valor inicial de 0."

Neste exemplo, o número máximo de tentativas de login permitido é declarado como uma constante, porque o valor máximo nunca muda. O contador de tentativa de login atual é declarado como uma variável, porque esse valor deve ser incrementado após cada tentativa de login que falhe.Você pode declarar várias constantes ou variáveis ​​em uma única linha, separadas por vírgulas:

Nota

Se um valor armazenado em seu código não for alterado, declare sempre como uma constante com o comando let. Use variáveis ​​apenas para armazenar valores que precisam ser capazes de mudar.

Type Annotations

Você pode fornecer uma type annotation (ou anotação de tipo) quando você declara uma constante ou variável para ser claro sobre o tipo de valores que a constante ou variável pode armazenar. Escreva uma anotação de tipo colocando dois pontos após o nome constante ou variável, seguido por um espaço e pelo nome do tipo a ser usado.

O exemplo abaixo fornece uma anotação de tipo para uma variável chamada welcomeMessage, para indicar que a variável pode armazenar somente valores do tipo String:

Os dois pontos na declaração significam "... do tipo ...", então o código acima pode ser lido como: "Declare uma variável chamada welcomeMessage que é do tipo String".

A frase "do tipo String" significa "pode ​​armazenar qualquer valor String". Pense nisso como "o tipo do valor" que pode ser armazenado e agora a variável pode receber qualquer valor do tipo String sem qualquer tipo de erro.

Você pode definir várias variáveis ​​relacionadas do mesmo tipo em uma única linha, separadas por vírgulas, com uma única anotação de tipo após o nome final da variável da seguinte maneira:

Nota

É raro que você precise escrever anotações de tipo na prática pois se você fornecer um valor inicial para uma constante ou variável no ponto em que são definidos, o Swift pode quase sempre inferir o tipo a ser usado para essa constante ou variável, conforme será descrito mais à frente. No exemplo de mensagem de boas-vindas acima, nenhum valor inicial é fornecido e, portanto, o tipo da variável welcomeMessage é especificado com uma anotação de tipo em vez de ser inferido a partir de um valor inicial.

Nomeando constantes e variáveis

Os nomes das constantes e variáveis ​​podem conter quase qualquer caractere, incluindo caracteres Unicode:

Os nomes das constantes e variáveis ​​não podem conter espaços em branco, símbolos matemáticos, setas, pontos Unicode de uso privado (ou inválidos) ou caracteres de desenho de linha. Nem podem começar com um número, embora os números possam ser incluídos em outro lugar dentro do nome.
Uma vez que você declarou uma constante ou variável de um determinado tipo, não é possível declarar novamente com o mesmo nome ou alterá-lo para armazenar valores de um tipo diferente. Nem você pode alterar uma constante para uma variável ou uma variável em uma constante.

Nota

Se você precisar declarar uma constante ou variável do mesmo nome que uma palavra-chave reservada do Swift, envolva a palavra-chave com aspas simples ````` ao usá-lo como um nome. No entanto, evite usar palavras-chave como nomes, a menos que você não tenha absolutamente nenhuma escolha.

Você pode alterar o valor de uma variável existente para outro valor de um tipo compatível. No exemplo abaixo, o valor da variável friendlyWelcome é alterado de "Olá!" para "Bonjour!"

Ao contrário de uma variável, o valor de uma constante não pode ser alterado uma vez que seja setado. A tentativa de fazer isso é relatada como um erro quando seu código é compilado:

Imprimindo variáveis e constantes

Você pode imprimir o valor atual de uma constante ou variável com a função print (_: separator: terminator: )

A função print(_: separator: terminator: ) é uma função global que imprime um ou mais valores para uma saída apropriada. No Xcode, por exemplo, a função print(_: separator: terminator: ) imprime sua saída no painel "console" do Xcode.

O parâmetro separador e terminador têm valores padrão, portanto, você pode omiti-los quando você chama essa função. Por padrão, a função encerra a linha que imprime adicionando uma quebra de linha. Para imprimir um valor sem uma quebra de linha após usar a função, passe uma string vazia como o terminador - por exemplo, print(someValue, terminator: ""). Veremos mais sobre parâmetros com valores padrão no decorrer do livro.

Swift usa a interpolação de Strings para incluir o nome de uma constante ou variável como um espaço reservado em uma string mais longa e para solicitar Swift para substituí-la pelo valor atual dessa constante ou variável. Envolva o nome entre parênteses e escape-o com uma barra invertida antes dos parênteses:

Nota

Todas as opções que podem ser usadas com o recurso de interpolação de Strings são descritas mais adiante do livro.

Comentários

Use comentários para incluir texto não executável em seu código, como uma nota ou lembrete para você sendo que os comentários são ignorados pelo compilador Swift quando seu código é compilado. Os comentários em Swift são muito semelhantes aos comentários em C, os de uma linha começam com duas barras diretas //:

Os comentários com mais de uma linha começam com uma barra direta seguida de um asterisco (/*) e terminam com um asterisco seguido por uma barra direta */:

Ao contrário dos comentários com mais de uma linha em C, em Swift podem ser aninhados dentro de outros comentários com mais de uma linha. Você escreve comentários aninhados iniciando um bloco de comentários e iniciando um segundo comentário no primeiro bloco. O segundo bloco é então fechado, seguido do primeiro bloco:

Os comentários com várias linhas permitem que você comente grandes blocos de código de forma rápida e fácil, mesmo que o código já contenha comentários.

Ponto-e-vírgula

Ao contrário de muitas outras linguagens, o Swift não exige que você escreva um ponto-e-vírgula ; após cada linha de código, embora você possa fazê-lo se desejar. No entanto, são necessários pontos e vírgulas se você quiser escrever múltiplas declarações separadas em uma única linha:

Integers

Os Integers(ou inteiros) são números inteiros sem vírgulas, como 42 e -23. Inteiros podem possuir sinais (positivos, zero ou negativos) ou não (positivo ou zero). O Swift fornece números inteiros com sinais e sem sinais em formas de 8, 16, 32 e 64 bits. Esses números inteiros seguem uma convenção de nomenclatura semelhante à linguagem C, na medida em que um inteiro sem sinal de 8 bits é do tipo UInt8 (a letra U é de unsigned) e um inteiro com sinal de 32 bits é do tipo Int32. Como todos os tipos no Swift, esses tipos de números inteiros possuem nomes que começam com letra maiúscula.

Limite do tipo Integer

Você pode acessar os valores mínimos e máximos de cada tipo de número inteiro com suas propriedades min e max:

Os valores dessas propriedades seguem o seu tipo (como UInt8 no exemplo acima) e, portanto, podem ser usados ​​em expressões de outros valores do mesmo tipo.

Int

Na maioria dos casos, você não precisa escolher um tamanho específico de um número inteiro para usar em seu código (como UInt8 ou Int32) pois o Swift fornece um tipo inteiro adicional, Int, que tem o mesmo tamanho que a arquitetura nativa da plataforma atual:

  • Nas plataformas 32-bits, o tipo Int tem o mesmo tamanho que um Int32

  • Nas plataformas 64-bits, o tipo Int tem o mesmo tamanho que um Int64

A menos que você precise trabalhar com um tamanho específico de inteiro, use sempreIntpara valores inteiros em seu código. Isso ajuda a consistência e interoperabilidade do código. Mesmo em plataformas de 32 bits, o tipo Int pode armazenar qualquer valor entre -2.147.483.648 e 2.147.483.647, e é grande o suficiente para muitos intervalos de números.

UInt

Swift também fornece um tipo inteiro sem sinal, UInt, que tem o mesmo tamanho que o tamanhoque a arquitetura nativa da plataforma atual:

  • Nas plataformas 32-bits, o tipo UInt tem o mesmo tamanho que um UInt32

  • Nas plataformas 64-bits, o tipo UInt tem o mesmo tamanho que um UInt64

Nota

Use UInt somente quando precisar especificamente de um tipo inteiro sem sinal com o mesmo tamanho que o tamanhoque a arquitetura nativa da plataforma atual. Se este não for o caso, o tipo Int é preferido, mesmo quando os valores a serem armazenados são conhecidos como não negativos. Um uso consistente de Int para valores inteiros ajuda a interoperabilidade de código, evita a necessidade de converter entre diferentes tipos de números e ajuda o código a inferir o tipo de forma clara (O tópico type-inference será discutido mais à frente).

Números do tipo Ponto Flutuante

Os números de ponto flutuante (Floating-Point) são números quebrados, ou com vírgulas, como 3,14159, 0,1 e -273,15. No padrão americano, as vírgulas são substituídas por pontos, como 3.14159, 0.1 e -273.15.

Os números dos tipos ponto flutuante podem representar uma gama muito maior de valores que os tipos inteiros e podem armazenar números que são muito maiores ou menores do que podem ser armazenados em um Int. O Swift fornece dois tipos de números de ponto flutuante com sinal (positivo ou negativo):

  • O tipo Double representa a versão 64-bits dos pontos flutuantes.

  • O tipo Float representa a versão 32-bits dos pontos flutuantes.

Nota

O tipo Double tem uma precisão de pelo menos 15 dígitos decimais, enquanto a precisão do Float pode ter apenas 6 dígitos decimais. O tipo de ponto flutuante apropriado para usar depende da natureza e do alcance dos valores com os quais você precisa trabalhar no seu código. Em situações em que qualquer tipo é apropriado, o Double é preferido.

Type Safety e Type Inference

Swift é uma linguagem type-safe (segura em relação aos seus tipos). Um linguagem type-safe encoraja você a ser claro sobre os tipos de valores com os quais o seu código pode funcionar, portanto, se parte do seu código requer uma String, você não pode passar um Int por engano.

Como o Swift é type-safe, ele executa verificações de tipo ao compilar seu código e sinaliza quaisquer tipos incompatíveis como erros. Isso permite capturar e corrigir erros o mais cedo possível no processo de desenvolvimento.

A checagem de tipo ajuda a evitar erros quando você está trabalhando com valores de diferentes tipos, no entanto, isso não significa que você precise especificar o tipo de cada constante e variável que você declara. Se você não especificar o tipo de valor que você precisa, Swift usa o type inference (inferência de tipo) para determinar o tipo apropriado.

O type inference permite que o compilador deduza o tipo de uma expressão automaticamente quando compila seu código, simplesmente examinando os valores que você fornece. Por causa da inferência de tipos, o Swift requer muito menos declarações de tipo que linguagens como C ou Objective-C. Constantes e variáveis ​​ainda são explicitamente digitadas, mas grande parte do trabalho de especificar seu tipo é descoberto pelo compilador e feito para você.

O type inference é particularmente útil quando você declara uma constante ou variável com um valor inicial. Isso geralmente é feito atribuindo um valor literal à constante ou variável no ponto em que você o declara. (Um valor literal é um valor que aparece diretamente no seu código fonte, como 42 e 3.14159 nos exemplos abaixo.)

Por exemplo, se você atribuir um valor literal de 42 a uma nova constante sem dizer que tipo é, Swift infere que você deseja que a constante seja do tipo Int, porque você inicializou ela com um número que parece um inteiro:

Da mesma forma, se você não especificar um tipo quando estiver escrevendo um valor literal de um número do tipo ponto flutuante, o Swift infere que você deseja criar um Double:

Swift sempre escolhe o tipo Doubleao invés de Floatquando compilador está inferindo o tipo de um número do tipo ponto flutuante. Se você combinar números literais inteiros e outros de ponto flutuante em uma expressão, o tipo Doubleserá inferido a partir do contexto:

O número literal 3 não possui nenhum tipo explícito por si só e, portanto, um valor do tipo Doubleé inferido a partir da presença de um número literal do tipo ponto flutuante como parte da soma.

Números Literais

Números inteiros literais podem ser escritos das seguintes formas:

  • Um número decimal sem nenhum prefixo.

  • Um número binário com o prefixo 0b.

  • um número octal com o prefixo 0o.

  • Um número hexadecimal com o prefixo 0x.

Portanto, seguindo a tabela acima todos os números abaixo tem o valor 17 na base decimal:

Os números literais do tipo ponto flutuante podem ser decimais (sem prefixo) ou hexadecimais (com um prefixo0x). Eles devem sempre ter um número decimal ou um número hexadecimal em ambos os lados do ponto decimal.

Quando estão na sua forma decimal, também podem ter um expoente opcional, indicado por uma letra e maiúscula ou minúscula. Já quando estão na sua forma hexadecimal, devem ter um expoente indicado por uma letra p maiúscula ou minúscula.

Para números decimais com um expoente e= exp, o número base e multiplicado por 10 elevado a exp:

  • 1.25e2 é o mesmo que 1.25 x 10 elevado a 2, ou 125.0.

  • 1.25e-2 é o mesmo que 1.25 x 10 elevado a 2, ou 0.0125.

Para números hexadecimais com um expoente p= exp, o número base e multiplicado por 2 elevado a exp:

  • 0xFp2 é o mesmo que 15 x 2 elevado a 2, ou 60.0.

  • 0xFp-2 é o mesmo que 15 x 2 elevado a -2, ou 3.75.

Todos esses números literais do tipo ponto flutuante têm um valor decimal de 12.1875:

Os números literais podem conter uma formatação extra para facilitar a leitura. Ambos os inteiros e pontos flutuante podem ser preenchidos com zeros extras e podem conter underscore para ajudar com legibilidade, porém nenhum tipo de formatação afeta o valor do número:

Conversão de tipos numéricos

Use o tipo Int para todas as constantes e variáveis ​​inteiras de um modo geral em seu código, mesmo que você saiba que elas não serão negativas (pois nesse caso um UInt serviria bem). Usar o tipo de número inteiro padrão em situações cotidianas significa que as constantes e variáveis ​​inteiras são imediatamente interoperáveis ​​em seu código e combinarão o tipo inferido para valores literais inteiros, em outras palavras não será necessário nenhum tipo de conversão de valores para trabalhar entre eles e as ações matemáticas como soma, subtração e etc serão sempre feitas da forma mais coerente e sem perca de resultados.

Use outros tipos inteiros somente quando eles são especificamente necessários para uma tarefa em questão, como dados de tamanho explícito de uma fonte externa, ou para desempenho, uso de memória ou outra otimização necessária. O uso de tipos de tamanho explícito (como UInt32 ou Int64) nessas situações ajuda a capturar qualquer vazamento de valor acidental e, implicitamente, documenta a natureza dos dados que estão sendo usados.

Conversões do tipo inteiro

O intervalo de números que podem ser armazenados em uma constante ou variável inteira é diferente para cada tipo numérico. Por exemplo, uma constante ou variável do tipo Int8 pode armazenar números entre -128 e 127, enquanto que uma constante ou variável UInt8pode armazenar números entre 0 e 255. Portanto, se você inserir um número que não se encaixa em uma constante ou variável de um tipo inteiro de um tamanho específico, o compilador dispara um erro quando seu código é compilado:

Como cada tipo numérico pode armazenar um intervalo diferente de valores, você deve optar pela conversão analisando caso a caso e inserindo a conversão explicitamente. Esta opção impede erros de conversão escondidos e ajuda a tornar as intenções de conversão de tipo explícitas em seu código.

Para converter um tipo de dado numérico específico para outro, você inicializa um novo número do tipo desejado com o valor existente. No exemplo abaixo, a constante twoThousandé do tipo UInt16, enquanto a constante one é do tipo UInt8. Eles não podem ser adicionados diretamente, porque não são do mesmo tipo, em vez disso, esse exemplo chama o construtor com o comandoUInt16(one) para criar um novo UInt16inicializado com o valor da constante one, e usa esse valor no lugar do original:

Como ambos os números da adição são agora do tipo UInt16, a adição é permitida. A constante de saída twoThandandAndOneé inferida para ser do tipo UInt16, porque é a soma de dois valores UInt16.

NomeDoTipo(comValorInicial) é a maneira padrão de chamar o inicializador de um tipo e passar um valor inicial. Por trás das cenas, o UInt16possui um inicializador que aceita um valor UInt8e, portanto, este inicializador é usado para criar um UInt16novo a partir de um UInt8que já existe.

Você não pode passar qualquer valor aqui, somente um tipo para o qual o UInt16fornece um inicializador. Estender tipos existentes para fornecer inicializadores que aceitam novos tipos (incluindo suas próprias definições de tipo) é discutido neste livro na sessão de Extensions.

Conversão entre inteiros e pontos flutuante.

As conversões entre os tipos numéricos inteiro e ponto flutuante devem ser explícitas:

Aqui, o valor da constante three é usado para criar um novo valor do tipo Double, de modo que ambos os números da adição sejam do mesmo tipo. Sem essa conversão, a adição não seria permitida.

A conversão de ponto flutuante para inteiro também deve ser explícita. Um tipo inteiro pode ser inicializado com um valor do tipo Doubleou Float:


Os números do tipo ponto flutuante são sempre truncados quando usados ​​para inicializar um novo valor inteiro dessa maneira. Isso significa que 4,75 torna-se 4 e -3,9 torna-se -3.

Nota

As regras para combinar constantes e variáveis que armazenam tipos numéricos ​​são diferentes das regras para valores literais numéricos. O número literal 3 pode ser adicionado diretamente ao número literal 0.14159, porque os literais numéricos não possuem um tipo explícito por si só, seu tipo é inferido apenas no ponto em que eles são avaliados pelo compilador.

Type Aliases

Os type aliases (ou apelidos de tipos) definem um nome alternativo (apelido) para um tipo existente. Você pode definir os type aliases com a comando typealias. Eles são úteis quando você deseja se referir a um tipo existente por um nome que seja contextualmente mais apropriado, como, por exemplo, quando se trabalha com dados de um tamanho específico de uma fonte externa:

Uma vez que você define um type alias, você pode usar o alias em qualquer lugar que use o nome original:

Aqui, o nome AudioSample é definido como um alias (apelido) para o tipo UInt16. Como é um alias, a chamada para AudioSample.min na verdade chama UInt16.min, que fornece um valor inicial de 0 para a variável maxAmplitudeFound.

Booleans

O Swift tem um tipo booleano básico, chamado Bool. Os valores booleanos são referidos como lógicos porque eles só podem ser verdadeiros ou falsos. Swift fornece dois valores constantes booleanos, true e false:

Os tipos das constantes orangesAreOrangee turnipsAreDeliciousforam inferidos como Bool pelo fato de serem inicializados com valores literais booleanos. Tal como acontece com Inte Double, você não precisa declarar constantes ou variáveis ​​como Boolse você as setou com os valores trueou falselogo que você as crie. A inferência de tipos ajuda a tornar o código Swift mais conciso e legível quando você inicializa constantes ou variáveis ​​com outros valores cujo tipo já é conhecido.

Os valores booleanos são particularmente úteis quando você trabalha com declarações condicionais, como o condicional if:

Declarações condicionais, como o comando if, são abordadas com mais detalhes na seção de controle de fluxo neste mesmo livro.

A segurança de tipo do Swift evita que valores não booleanos sejam usados como Bool. O exemplo a seguir dispara um erro em tempo de compilação:

Já o exemplo abaixo funciona perfeitamente bem:

O resultado da comparação i == 1 é do tipo Bool, portanto o exemplo acima passa pela verificação de tipo. Comparações como i == 1 serão discutidas na seção de operadores básicos.

Tal como acontece com outros exemplos de type safety no Swift, esta abordagem evita erros acidentais e garante que a intenção de uma determinada seção de código seja sempre clara.

Tuples

Em Swift, tuples (ou tuplas) agrupam múltiplos valores em um único valor composto, sendo que dentro da tupla eles podem ser de qualquer tipo e não precisam ter o mesmo tipo entre si.

Neste exemplo, (404, "Not Found") é uma tupla que descreve um código HTTP. Um código de status HTTP é um valor especial retornado por um servidor web sempre que você solicitar uma página da Web. Um código de status de 404 Not Found é retornado se você solicitar uma página da Web que não existe.

A tupla (404, "Not Found") agrupa um Int e uma String para fornecer o código do status HTTP com dois valores separados: um número e uma descrição legível para humanos, portanto esse valor pode ser descrito como "uma tupla do tipo (Int, String)".

Você pode decompor o conteúdo de uma tupla em constantes ou variáveis ​​separadas, para que você então acessa como de costume:

Se você só precisar de alguns dos valores da tupla, ignore partes da tupla colocando um underline _ quando você decompõe a tupla:

Também é possível acessar os valores dos elementos individualmente em uma tupla usando números de índice começando em zero:

Você pode nomear os elementos individuais em uma tupla quando a tupla está sendo definida:

Se você nomear os elementos em uma tupla, você pode usar os nomes dos elementos para acessar os valores desses elementos:

As tuplas são particularmente úteis como valores de retorno das funções quando você precisa retornar mais de um único valor sem precisar criar classes ou estruturas para isso. Uma função que tenta recuperar uma página da Web pode retornar uma tupla do tipo(Int, String)para descrever o sucesso ou a falha na recuperação da página. Ao retornar uma tupla com dois valores distintos, cada um de um tipo diferente, a função fornece informações mais úteis sobre seu resultado do que se ela pudesse retornar apenas um único valor de um único tipo. Falaremos mais a respeito disso no capítulo de funções.

Nota

As tuplas são úteis para grupos de valores temporários e não são adequados para a criação de estruturas de dados complexas. Se a sua estrutura de dados provavelmente persistirá além de um escopo temporário, você deve modelá-la como uma classe ou estrutura, e não como uma tupla.

Optionals

Você deve usar optionals (ou opcionais) em situações em que um valor pode não existir. Um opcional representa duas possibilidades: Ou há um valor, e você pode desempacotar o opcional para acessar esse valor, ou não há um valor.

Nota

O conceito de opcionais não existe nas linguagens C ou Objetive-C. O conceito mais próxima em Objective-C é a capacidade de retornar nulo de um método que retornaria um objeto, com o comando nil significando "a ausência de um objeto válido". No entanto, isso só funciona para objetos e não funciona para estruturas, tipos básicos da linguagem ou valores de enums. Para esses tipos, os métodos em Objetive-C normalmente retornam um valor especial (como NSNotFound) para indicar a ausência de um valor. Esta abordagem pressupõe que o quem chama o método sabe que há um valor especial para testar, portanto é preciso verificar isso. Os opcionais do Swift permitem que você indique a ausência de um valor para qualquer tipo, sem a necessidade de constantes especiais.

Aqui está um exemplo de como os opcionais podem ser usados ​​para lidar com a ausência de um valor. O tipo Int do Swift possui um inicializador que tenta converter um valor String em um valor do tipo Int. No entanto, nem todas as seqüências de caracteres podem ser convertidas em um número inteiro. A string "123" pode ser convertida num valor numérico 123, mas a string "hello, world" não possui um valor numérico óbvio para se converter. O exemplo abaixo usa o inicializador para tentar converter uma String em um Int:

Como o inicializador pode falhar, ele retorna um valor Int opcional, em vez de um Int normal. Um Int opcional é escrito como Int?, não Int. O ponto de interrogação indica que o valor que ele contém é opcional, o que significa que pode conter algum valorInt, ou pode não conter nenhum valor, porém ele não pode conter nada mais, como um valor Bool ou um valor String. É um Int ou não é nada.

Nil

Você seta uma variável opcional para um estado sem valor atribuindo-lhe o valor especial nil (nulo):

Nota

Você não pode usar o operador nil com constantes e variáveis ​​não-opcionais. Se uma constante ou variável em seu código precisa funcionar com a ausência de um valor sob certas condições, declare sempre como um valor opcional do tipo apropriado.

Se você definir uma variável opcional sem fornecer um valor padrão, a variável será automaticamente setada como nula para você:

Nota

O nil do Swift não é o mesmo que o nil do Objective-C. Em Objective-C, nil é um ponteiro para um objeto inexistente. Em Swift, nil não é um ponteiro, é a ausência de um valor de um determinado tipo. Opcionais de qualquer tipo podem ser definidos como nulas, como valores de estruturas ou enums, e não apenas tipos de classes (conhecidos como objetos).

Comandos if e unwrapping forçado

Você pode usar o comando if para descobrir se um opcional contém um valor comparando o opcional com o nulo utilizando o operador nil. Você pode fazer essa comparação com o operador "igual a" == ou o operador "não igual a" ou “diferente de” !=. Se um opcional tiver um valor, ele é considerado "não igual a" ou "diferente de" nulo:

Uma vez que você tenha certeza de que o opcional contém um valor, você pode acessar seu valor interno, adicionando um ponto de exclamação ! ao final do nome do opcional, que quer dizer: "Eu sei que esse opcional definitivamente tem um valor, portanto use-o." Isso é conhecido como forced unwrapping(forçar o desembrulho) do valor opcional:

Falaremos mais a respeito do comando if e como ele funciona na seção de controle de fluxo nesse mesmo livro.

Nota

Tentar usar o operador ! para acessar um valor opcional inexistente irá disparar um erro em tempo de execução. Certifique-se sempre de que um opcional contém um valor não-nulo antes de usar o operador ! para forçar o desembrulho do seu valor.

Optional Binding

Você pode usar o optional binding (ligação de dados opcionais) para descobrir se um opcional contém um valor e, em caso afirmativo, disponibilizar esse valor em uma constante ou variável temporária. O optional binding pode ser usado com comandos if e while para verificar o valor dentro de um opcional e extrair esse valor em uma constante ou variável em um único comando. Os comandos if e while são descritas em mais detalhes neste livro na seção de Fluxo de controle. Escreva um optional binding com o comando if da seguinte maneira:

Você pode reescrever o exemplo que utilizamos a variável possibleNumber bem acima na seção Optionals para usar o optional binding em vez do forced unwrapping:

Esse codigo pode ser lido como: "Se o Int opcional retornado pelo construtor de Int(possibleNumber) contém um valor, então defina uma nova constante chamada actualNumber para o valor dentro do opcional".

Se a conversão for bem-sucedida, a constante actualNumber poderá ser usada dentro do bloco if. Ela já foi inicializada com o valor contido no opcional e, portanto, não há necessidade de usar o comando ! para acessar seu valor. Neste exemplo, ela é usada simplesmente para imprimir o resultado da conversão.

Você pode usar constantes e variáveis ​​com o optional binding. Se você quiser manipular o valor de actualNumber dentro do bloco if, você poderia escrever da seguinte maneira: if var actualNumber e o valor contido no opcional seria disponibilizado como uma variável em vez de uma constante.

Você pode incluir tantos optional bindings e condições booleanas em um único comando if quanto você quiser, separados por vírgulas. Se algum dos valores em algum dos optional bindings for nulo ou qualquer condição booleana seja avaliada como falsa, a condição inteira do comando if será considera falsa. O seguintes comandos if são equivalentes e realizam a mesma ação:

Nota

Constantes e variáveis ​​criadas com optional binding em um comando if são disponíveis somente dentro do corpo do comando if. Em contraste, as constantes e variáveis ​​criadas com o comando guard estão disponíveis nas linhas de código abaixo da declaração do comando, conforme estudaremos na seção de Early Exit.

Optionals com unwrap implícito

Conforme descrito acima, os opcionais indicam que uma constante ou variável pode não ter valor. Os opcionais podem ser verificados com o comando if para ver se existe um valor e podem ser condicionalmente desembrulhados com o optional binding para acessar o valor dentro do opcional se existir.

Às vezes, pela estrutura de um programa fica claro que um opcional sempre terá um valor depois que ele for setado pela primeira vez. Nestes casos, é útil remover a necessidade de verificar e desembrulhar o valor do opcional sempre que a variável ou constante é acessada, pois podemos ter certeza de forma segura que existe um valor lá dentro o tempo todo.

Esses tipos de opcionais são definidos como implicitly unwrapped optionals(opcionais com unwrap implícito). Você pode escrever um opcional com unwrap implícito colocando um ponto de exclamação String! em vez de um ponto de interrogação String? após o tipo que você deseja que se torne opcional.

Os opcionais implementados de forma implícita são úteis quando o valor de um opcional é confirmado para existir depois que o mesmo for criado e definitivamente pode ser assumido que existirá valor dentro dele em todos os pontos em que for acessado. O uso principal deopcionais com unwrap implícitoem Swift é durante a inicialização de uma classe, conforme descrito nas seções de Referências desconhecidas e Propriedades opcionais com unwrap implícito.

Um opcional com unwrap implícito é um opcional normal atrás dos bastidores, mas também pode ser usado como um valor normal (nao-opcional) sem a necessidade de desembrulha-lo cada vez que ele é acessado. O exemplo a seguir mostra a diferença de comportamento entre uma String opcional padrão e uma String opcional definida com unwrap implícito ao acessar seu valor:

Você pode pensar em um opcional com unwrap implícito como você dando permissão para que o opcional seja desembrulhado automaticamente sempre que for usado. Em vez de colocar um ponto de exclamação após o nome do opcional, cada vez que você o usa, você deve inserir um ponto de exclamação ! após o tipo de opção opcional quando você está declarando ele.

Nota

Se um opcional com unwrap implícito for nulo e você tentar acessar seu valor, ele irá disparar um erro de tempo de execução. O resultado é exatamente o mesmo se você utilizar um ponto de exclamação após um opcional normal para desembrulha-lo e ele não conter nenhum valor.

Você ainda pode tratar um opcional com unwrap implícito como um opcional normal, para verificar se ele contém um valor:

Você também pode usar um opcional com unwrap implícito com o recurso de optional binding, para verificar e desempacotar seu valor em uma única declaração:

Nota

Nunca use um opcional com unwrap implícito se existe a possibilidade dele se tornar nulo em algum ponto do seu código. sempre utilize um opcional normal se você precisa trabalhar em algum momento com o valor nulo durante o ciclo de vida da variável ou constante.

Tratamento de erros

Você deve fazer uso do tratamento de erros para responder às condições de erro que seu programa pode encontrar durante a execução.Em contraste com os opcionais que podem usar a presença ou a ausência de um valor para comunicar sucesso ou falha de uma função, o tratamento de erros permite determinar a causa específica da falha e, se necessário, propagar o erro para outra parte do seu programa.

Quando uma função encontra um uma condição de erro, nós dizemos que ela lança um erro, ou throws an error em inglês, portanto quem chamar essa função pode então capturar o erro e tratar adequadamente.

Uma função indica que ela pode lançar um erro, incluindo o comando throw sem sua declaração. Quando você chama uma função que pode lançar um erro, você deve utilizar o comando try seguido da chamada da função. O Swift automaticamente propaga erros fora de seu escopo atual até que sejam tratados por uma bloco catch.

O comando do cria um novo bloco de código que permite que os erros sejam propagados para uma ou mais blocos catch. Abaixo está um exemplo de como o tratamento de erros pode ser usado para responder a diferentes condições de erro:

E para usar a função, basta chamá-la da seguinte maneira:

Neste exemplo, a função makeASandwich() irá disparar um erro se nenhum prato limpo estiver disponível ou se algum dos ingredientes estiverem faltando. Como makeASandwich() pode lançar um erro, a chamada de função deve ser antecedida pelo comando try. Ao envolver a chamada de função em um bloco do, qualquer erro que seja lançado será propagado para as blocos catch fornecidos.

Se nenhum erro for lançado, a função eatASandwich() é chamada. Se um erro for lançado e corresponder ao caso SandwichError.outOfCleanDishes, a função washDishes() será chamada, se um erro for lançado e corresponder ao caso SandwichError.missingIngredients, a funçãobuyGroceries() é chamada com o valor [String] capturado pelo bloco catch. Lançar, capturar e propagar erros é discutido com mais detalhes ao decorrer do livro.

Assertions e Preconditions

Assertions e Preconditions são verificações que ocorrem em tempo de execução. Você os usa para garantir que uma condição essencial seja satisfeita antes de executar qualquer outro código. Se a condição booleana durante o assertion ou o precondition for verdadeira, a execução do código continua normalmente, mas se a condição for avaliada como falsa, o estado atual do programa é invalidado, a execução do código termina e seu aplicativo é encerrado.

Você deve usar assertions e preconditions para expressar a suposição que você faz e as expectativas que você tem enquanto está criando seu código, portanto você pode incluí-las como parte de seu código. As assertions ajudam você a encontrar erros e suposições incorretas durante o desenvolvimento, e as preconditions o ajudam a detectar problemas em produção.

Além de verificar suas expectativas em tempo de execução, as assertions e preconditions também se tornam uma forma útil de documentação dentro do código. Ao contrário do tratamento de erro discutido na seção acima, as assertions e preconditions não são usadas para erros que podem ser tratados ​​ou esperados. Como uma assertion ou precondition que falha indica um estado de programa inválido, não há como capturá-las dentro de um bloco catch.

O uso de assertions e preconditions não serve para projetar seu código de tal forma que as condições inválidas nunca possam surgir. No entanto, utiliza-los para impor dados e estados válidos faz com que seu aplicativo termine mais previsivelmente se ocorrer um estado inválido e ajuda a tornar o problema mais fácil de debugar. A interrupção da execução assim que um estado inválido é detectado também ajuda a limitar o dano causado pelo mesmo.

A diferença entre assertions e preconditions é notada quando são checadas no seu código: As assertions são checadas apenas em compilações do tipo debug, já as preconditions são checadas nas compilações de debug e produção. Nas compilações de produção, a condição dentro de uma assertion é ignorada. Isso significa que você pode usar tantas assertions quanto quiser durante seu processo de desenvolvimento, sem impactar o desempenho na produção.

Debugando com Assertions

Você pode usar uma assertion chamando a função assert(_: _: file: line: ) da biblioteca padrão do Swift. Você passa para esta função uma expressão booleana que é avaliada como verdadeira ou falsa e uma mensagem para exibir se o resultado da condição for falso. Por exemplo:

Neste exemplo, a execução do código continua se a expressão age >= 0 for verdadeiro, ou seja, se o valor da idade não for negativo. Se o valor da idade for negativo, como no código acima, age >= 0 é avaliada como falsa e a assertion falha, encerrando o aplicativo. Você pode omitir a mensagem da assertion, por exemplo, quando a condição booleana é clara o suficiente.

Se o código já verifica a condição booleana antes de você usar a função assertion, você pode usar a função assertionFailure(_: file: line: ) para indicar que uma já falhou. Por exemplo:

Aplicando Preconditions

Use uma precondition sempre que uma condição tenha chance de ser avaliada como false, mas definitivamente precisa ser true para o seu código continuar a execução. Por exemplo, use uma precondition para verificar se um um índice de um array não está fora dos limites ou para verificar se foi passado um valor válido como parâmetro para uma função.
Para usar uma precondition use a função precondition(_: _: file: line: ). Você deve passar para esta função uma expressão que é avaliada como true ou false e uma mensagem para exibir se o resultado for false, por exemplo:

Você também pode chamar a função preconditionFailure(_: file: line: ) para indicar que já ocorreu uma falha, por exemplo, se o case default de um bloco switch foi chamado quando na verdade todos os dados de entrada deveriam ser tratados por um outros blocos case.

Nota

Se você compilar seu código no modo unchecked passando o parâmetro -Ounchecked no momento da compilação, as preconditions não são verificadas. O compilador assume que as preconditions sempre serão verdadeiras e otimiza seu código para isso. No entanto, a função fatalError(_: file: line: ) sempre interrompe a execução, independentemente das configurações de otimização e compilação. Você pode usar a função fatalError(_: file: line: ) durante a prototipagem e o desenvolvimento inicial para criar um stub (uma parada num código com um aviso) para funcionalidades que ainda não foram implementadas, escrevendo fatalError("Não Implementado") como a implementação do stub. Como os erros fatais nunca são otimizados, ao contrário de assertions ou preconditions, você pode ter certeza de que a execução sempre irá ser interrompida se encontrar uma implementação de algum stub.

results matching ""

    No results matching ""