Enumerations

Um enumeration (ou enumeração em português) define um tipo comum para um grupo de valores relacionados e permite que você trabalhe com esses valores de maneira segura no seu código.

Se voce está familiarizado com a linguaguem C, você sabe que enumerations em C atribuem nomes relacionados a um conjunto de valores inteiros. Enumerations no Swift são muito mais flexíveis e não precisam fornecer um valor para cada bloco case da enumeração. Se um valor (conhecido como valor "raw") for fornecido para cada case do enumaration, o valor pode ser uma string, um caractere ou um valor de qualquer tipo inteiro ou ponto flutuante.

Alternativamente, os cases de um enumeration podem especificar valores associados de qualquer tipo para serem armazenados junto com cad case. Você pode definir um conjunto comum de cases relacionados como parte de um enumeration, cada um tem um conjunto diferente de valores de um tipo específico associado consigo.

Enumerations no Swift são tipos de primeira classe por direito próprio. Eles adotam muitos recursos tradicionalmente suportados somente por classes, como propriedades computadas para fornecer informação adicional sobre o valor atual de um enumeration, e métodos de instância para fornecer funcionalidades relacionadas aos valores que o enumaration representa. Eles também podem definir inicializadores para fornecer um valor inicial de um case, podem ser extendidos para expandir suas funcionalidades além da implementação original e pode implementar protocolos para fornecer funcionalidades padrão.
Para saber mais a respeito desses termos veja as seções Propriedades, Métodos, Inicialização, Extensões e Protocolos.

Sintaxe dos Enumarations

Você pode criar enumerations com o comando enum e colocando a definição inteira entre um par de chaves:

Aqui temos um exemplo de um enumeration que representa quatro pontos de uma bússola:

Os valores definidos em um enumeration (como north, south, east e west) são seus cases. Você deve usar o comando case para criar um novo case de um enumeration.

Nota

Diferentemente da linguagem C e Objective-C, os cases de um enumeration no Swift não são atribuídos para um valor padrão do tipo Int quando são criados. No enum CompassPoint acima, north, south, east e west não são implicitamente 0, 1, 2 e 3. Em vez disso, os diferentes cases de um enum são valores reais em seu próprio direito, com um tipo explicitamente definido de CompassPoint.

Vários cases podem aparecer em uma única linha, separados por vírgulas:

Cada definição de um enumeration define um tipo novo com sua marca. Como outros tipos no Swift, seus nomes (como CompassPoint e Planet) devem começar com letra maiúscula. Sempre dê para um enumeration nomes no singular ao invés de nomes no plural, para que eles sejam lidos como auto evidentes:

O tipo da variável directionToHead é inferido quando ela é inicializada com um dos valores possíveis do enum CompassPoint. Uma vez que a variável directionToHead é declarada como sendo do tipo CompassPoint, você pode atribuir para ela um valor diferente do mesmo enum usando uma sintaxe mais curta com somente o ponto:

O tipo da variável directionToHead já é conhecido, e portanto você pode ignorar o tipo quando está setando um valor para ela. Isso torna o código altamente legível enquanto você trabalha com valores de enumerations tipados.

Match em valores de Enumerations com o comando switch.

Você pode dar mach em valores individuais de um enum com o comando switch:

Você pode ler o código acima como: "Considere o valor da variáveis directionHead. Caso ele for igual a .north, imprima "Lots of planets have a north". Caso ele for igual .south, imprima "Watch out for penguins" e assim por diante.

Como descrito na seção Controle de Fluxo, um comando switch deve ser exaustivo quando está considerando os cases de um enumeration. Se o case para .west for omitido, o código não irá compilar porque ele não considera a lista completa de cases do enum CompassPoint. Exigir a exaustividade garante que os cases de um enumaration não serão omitidos por acidente.

Quando não é apropriado fornecer um case para cada case do enumeration, você pode fornecer um bloco default para cobrir todos os cases que não estão declarados explícitamente:

Valores Associados

Os exemplos da sessão anterior mostram como os cases de um enumeration são uma definição (e tipagem) de um valor no seu próprio tipo. Você pode setar uma constante ou variável para receber Planet.earth, e verificar esse valor mais tarde. No entanto às vezes é útil poder armazenar valores (associados) de outros tipos ao lado desses cases. Isso permite que você armazene informação customizada adicional junto com o próprio case, e permite que essa informação varie toda vez que você usar o case no seu código.

Você pode criar enumerations para armazenar valores associados de um tipo específico, e os tipos dos valores podem ser diferentes para cada case se necessário. Em outras linguagens, enumerations similares a esses são conhecidos como uniões discriminadas, uniões marcadas ou variantes em outras linguagens de programação.

Por exemplo, vamos supor que um sistema de controle de estoque precisa controlar seus produtos por meio de dois tipos diferentes de código de barras. Alguns produtos receberam código de barras 1D no formato UPC, que usa números de 0 a 9. Cada código de barras tem um dígito que representa o número do sistema, seguido de 4 dígitos que representam o código de manufatura e 5 dígitos que representam o código do produto. Por fim, temos um dígito de verificação para verificar se o código foi escaneado corretamente.

Outros produtos receberam código de barras 2D no formato QRCode, que pode usar qualquer caractere ISO 8859-1 e pode fazer encode de uma string com mais de 2.953 caracteres de tamanho:

Seria conveniente pra um sistema de controle de estoque poder armazenar os código de barras UPC como tuplas de quatro valores do tipo Int, e código e barras QRCode como uma String de qualquer tamanho.

No Swift, um enumeration que define código de barras de produtos de ambos os tipos pode ser criado da seguinte maneira:

O código acima pode ser lido da seguinte maneira:

"Crie um enumeration chamado Barcode, que recebe um valor do tipo upc com o valor associado do tipo (Int, Int, Int, Int) ou um valor do tipo qrCode com um valor associado do tipo String."
A definição por si não fornece qualquer valor do tipo Int ou String, ele só define o tipo do valor, ela só efine o tipo dos valores associados que variáveis e constantes do tipo Barcode pode armazenar quando elas forem iguais à Barcode.upc ou Barcode.qrCode.
Agora, novos valores do tipo Barcode podem ser criados usando ambos os tipos:

Este exemplo cria uma nova variável chamada productBarcode e seta para ela um valor de Barcode.upc com uma tupla como valor associado de (8, 85909, 51226, 3).

O mesmo produto pode ser setado para um tipo diferente de Barcode:

Neste momento, o valor original de Barcode.upc e seus valores inteiros são substituídos pelo novo valor de Barcode.qrCode e seu valor do tipo String. Constantes e variáveis podem armazenar ambos os valores .upc ou .qrCode (junto com seus valores associados), mas elas só podem armazenar um deles em um dado momento.

Os diferentes tipos de Barcode podem ser analisados usando um comando switch, como anteriormente. Nesse momento, entretanto os valores associados podem ser extraídos como parte do comando switch. Você pode extrair cada valor associado como uma constante (com o comando let) ou uma variável (com o comando var) para usar dentro do corpo do bloco case do switch:

Se todos os valores associados de um case de um enumeration forem extraídos como constantes ou variáveis, você pode coloar um único comando var ou let antes do nome do case, para deixar a sintaxe mais legível:

Valores Brutos

O exemplo acima mostra como os cases de um enumeration podem declarar que armazenam valores associados de diferentes tipos. Como uma alternativa para esses valores, os cases de um enumeration podem vir pré-populados com valores pardão (chamados de valores brutos), que são todos do mesmo tipo.

Aqui temo um exemplo que armazena valores brunos do tipo ASCII junto com os cases de um enumeration:

No exemplo acima, os valores brutos de um enum chamado ASCIIControlCharacter são definidos para serem do tipo Character e são setados para os caracteres de controle mais comuns. O tipo Character é descrito na seção Strings e Caracteres.

Valores brutos podem ser do tipo String, Character, Int, Double, Float entre outros. Cada valor bruno deve ser único dentro da declaração do enumeration.

Nota

Valores brutos não são o mesmo que valores associados. Valores brutos são setados para popular valores quando você define o seu enumeration no seu código, como os três códigos ASCII acima. O valor bruto para um case em particular é sempre o mesmo. Valores associados são setados quando você cria uma nova constante ou variável baseado em um dos cases do enumeration, e podem ser diferentes cada vez que você faz isso.

Valores Brutos Implícitos

Quando você está trabalhando com enumerations que podem armazenar inteiros ou strings como valores brutos, você não precisa setar explícitamente um valor bruto para cada case, e quando você não faz isso, o Swift vai automaticamente setar os valores para você.

Para instâncias onde inteiros são usados como valores brutos, o valor implícito para cada case é 1 + o valor do case anterior. Se o primeiro case não tem um valor setado, seu valor será 0.

O enum abaixo é uma redefinição do enumeration Planet, com valores brutos do tipo Int para representar a ordem de cada planeta no sistema solar:

No exemplo acima, Planet.mercury tem um valor bruto explícito de 1, Planet.venus tem um valor bruto implícito de 2 e assim por diante.
Quando Strings são usadas como valores brutos, o valor implícito de cada case é um texto com o nome do case.

O enum abaixo é uma redefinição do enumeration CompassPoint definido anteriormente, mas dessa vez com valores brutos do tipo String para representar o nome de cada direção:

No exemplo acima, CompassPoint.south tem um valor bruto implícito de "south", e assim por diante. Você pode acessar os valores brutos do case de um enumeration acessando sua propriedade rawValue:

Inicializando um Enumeration a partir de um valor bruto

Se você criar um enumeration que tenha valores brutos, ele automaticamente receberá um inicializador que recebe um valor do tipo do valor bruto (como um parâmetro chamado rawValue) e pode retornar um case do enum ou nil. Você pode usar esse inicializador para tentar criar uma nova instância do enumeration. Este exemplo identifica o planeta Urânio a partir do seu valor bruto 7:

Nem todos os valores do tipo Int possíveis vão achar um planeta correspondente, portanto, por causa disso, um inicializador que recebe um valor bruto sempre retorna um case opcional do enumeration. No exemplo acima, possiblePlanet é do tipo Planet?, ou "Planet opcional".

Nota

O inicializador que recebe um valor bruto é um inicializador falhável, porque nem todo valor bruto vai retornar um case do enumeration. Para mais informações veja a seção Inicialização.

Se você tentar achar um planeta na posição 11, o valor opcional retornado pelo inicializador do valor bruto será nil.

Este exemplo usa binding opcional para tentar acessar um planeta com o valor bruto 11. O comandoif let somePlanet = Planet(rawValue = 11) cria um opcional de Planet e seta a constante somePlanet para este valor se ele puder ser recuperado. Neste caso, não é possível recuperar um planeta com a posição 11, portantoo bloco else é executado.

Enumerations Recursivos

Um enumeration recursivo é um enum que tem outra instância de enumeration como um valor associado para um ou mais cases do enum. Você indica que um case de um enumeration é recursivo com o comando indirect antes dele, o que diz ao compilador para inserir a camada necessária.

Por exemplo, aqui temos um enumeration que armazena simples expressões aritiméticas:

Você também pode usar o comando indirect antes do começo da declaração do enumeration para habilitar a recursividade para todos os cases que precisarem dela:

Este enumeration pode armazenar 3 tipos de expressões aritiméticas: um número normal, a adição de duas expressões e a multiplicação de duas expressões. Os cases addition e multiplication tem valores aassociados que também são do tipo ArithmeticExpression, portanto permitem aninhas expressões. Por exemplo, a expressão (5 + 4) * 2 tem um número no lado direito da multiplicação e outra expressão no lado esquerdo da multiplicação. Como os dados estão aninhados, o enum usado para armazenar os dados também precisa suportar o aninhamento, isto significa que o enum precisa ser recursivo. O código abaixo mostra a recursividade do enumeration ArithmeticExpression sendo criada para a expressão (5 + 4) * 2:

Uma função recursiva é a maneira mais direta para trabalhar com dados que possuem uma estrutura recursiva. Por exemplo, aqui temos uma função que processa uma expressão aritmética:

Esta função processa um número simples simplesmente retornando o valor associado a ele. Ele calcula uma adição ou multiplicação processando a operação do lado esquerdo, depois processando a operação do lado direito e então, adicionando ou multiplicando ambos juntos.

results matching ""

    No results matching ""