Classes e Structures

Classes e structures (em português estruturas) são construções flexíveis e de propósito geral que se tornam os blocos de construção do código do seu programa. Você pode criar propriedades e métodos para adicionar funcionalidades às suas classes e structures usando exatamente a mesma sintaxe que você usa para constantes, variáveis ​​e funções.

Ao contrário de outras linguagens de programação, o Swift não exige que você crie um arquivo de interface separado do arquivo da implementação para suas classes e structures personalizadas. No Swift, você pode criar uma classe ou structure em um único arquivo, e a interface externa é automaticamente disponível para outra parte do seu código usar.

Nota

Uma instância de uma classe é tradicionalmente conhecida como objeto. Porém, classes e structures no Swift são muito mais parecidas em questão de funcionalidades do que em outras linguagens, portanto muitas partes desse capítulo descrevem funcionalidades que podem ser aplicadas para instâncias de ambas (classes e structures). Por este motivo, o termo instância é usada de uma forma geral.

Comparando Classes e Structures

Classes e structures em Swift possuem muitas coisas em comum, ambas podem:

  • Definir propriedades para armazenar valores.
  • Definir métodos para fornecer funcionalidades.
  • Definir subscripts para fornecer acesso aos seus valores usando a sintaxe de subscript.
  • Definir inicializadores para setar um estado inicial.
  • Ser estendido para expandir suas funcionalidades além da implementação padrão.
  • Adotar conformidade para protocolos para fornecer funcionalidades padrão de um certo tipo.

Para mais informações, veja as seções Propriedades, Métodos, Subscripts, Inicialização, Extensões e Protocolos.

Nota

Structures sempre são copiadas quando são passadas de um lugar para outro no seu código, e não usam o sistema de contagem de referência.

Sintaxe de definição

Classes e structures tem uma sintaxe de definição parecidas. Você deve criar classes com o comando class e structures com o comando struct. Ambos devem ter sua definição inteira dentro de um par de chaves:

Nota

Sempre que você definir uma nova classe ou structure, você está eficientemente criando uma novo tipo no Swift. Forneça para esses tipos nomes que tenham a sintaxe UpperCamelCase, que começam com letra maiúscula a cada palavra (como SomeClass e SomeStructure por exemplo). Por outro lado, sempre dê nomes lowerCamelCase (que começam com letra minúscula e cada palavra se inicia com letra maiúscula) para propriedades e métodos como frameRate e incrementCount por exemplo, para diferenciar eles dos nomes das classes e structures.

Aqui temos um exemplo da definição de uma structure e de uma classe:

O exemplo acima define uma nova structure chamada Resolution, para descrever a resolução de um monitor baseado em pixels. Ela tem duas propriedades armazenadas chamadas width e height. Propriedades armazenadas são constantes ou variáveis que são empacotadas e armazenadas como parte da classe ou structure. Essas duas propriedades são inferidas para serem do tipo Int, pois setamos para elas o valor inicial de 0.

O exemplo também define uma nova classe chamada VideoMode, para descrever uma configuração de vídeo específica para um monitor. Esta classe tem quatro propriedades armazenadas como variáveis. A primeira, resolution, é inicializada com uma nova instância da structure Resolution, que infere o tipo mais apropriado para a variável. Para as outras 3 propriedades, as novas instâncias de VideoMode serão inicializadas com o valor de interlaced para false (significa que o monitor não possui suporte a frame rate dobrado sem custo extra de processamento), a variável frameRate com o valor de 0.0 e uma String opcional chamada name. A propriedade name automaticamente recebe um valor padrão de nil, que significa que ela não possui nenhum valor, porque é do tipo opcional.

Instâncias de classes e structures

A definição da structure Resolution e da classe VideoMode apenas descrevem como uma instância delas serão. Por si só elas não descrevem uma instância criada de ambos. Para fazer isso você precisa criar uma instância de uma structure ou de uma classe.
A sintaxe para criar instâncias é idêntica para classes e structures:

Classes e structures usam esta sintaxe de inicialização para criar novas instâncias. A forma mais simples dela usa o nome da classe ou structure seguido de um par de parenteses vázio, como Resolution() ou VideoMode(). Este comando cria uma instância com todas as suas propriedades inicializadas para seus valores padrão. Inicialização de classes e structures serão abordados em mais detalhes na sessão Inicialização.

Acessando propriedades

Você pode acessar as propriedades de uma instância usando a sintaxe de ponto (.). Nesta sintaxe, você deve escrever o nome da propriedade imediatamente depois do nome da instância, separado por uma ponto (.), sem nenhum espaço.

Neste exemplo, someResolution.width faz referência a propriedade width da instância de someResolution, e retorna um valor padrão de 0.

Você pode navegar entre as sub-propriedades, como a propriedade width que está dentro da propriedade resolution da classe VideoMode:

Também é possível usar a sintaxe de ponto para atribuir um novo valor para uma propriedade:

Nota

Diferente do Objective-C, o Swift permite que você sete sub-propriedades de uma propriedade de uma structure diretamente. No último exemplo acima, a propriedade width da propriedade resolution da instância someVideoMode é setada diretamente, sem precisar setar a propriedade resolution inteira para um novo valor.

Inicializadores Memberwise para Structures

Toda structure tem um inicializador memberwise gerado automaticamente para que você possa usar para inicializar as propriedades de uma instância de uma structure. Os valores iniciais para as propriedades de uma nova instância podem ser passados pelo inicializador memberwise usando seu nome:

Diferente de structures, as instâncias de classes não recebem um inicializador memberwise por padrão. Inicializadores são descritos com mais detalhes na seção Inicializadores.

Structures e Enumerations são tipos de valor

Um tipo de valor é um tipo no qual seu valor é copiado quando é atribuído a uma variável ou constante, ou quando é passado para uma função.
Você tem usado extensivamente os tipos de valor ao longo dos capítulos anteriores. Na verdade, todos os tipos básicos do Swift (inteiros, números com ponto flutuante, booleanos, strings, arrays e dictionaries) são tipos de valor e são implementados como structures por trás dos panos.

Todas as structures e enumerations são tipos de valor no Swift. Isso significa que qualquer instância de uma structure ou enumeration que você criar (e qualquer tipo de valor que eles tiverem como propriedade) são sempre copiadas quando são passados através do seu código.
Considere o exemplo abaixo, que usa a structure Resolution dos exemplos anteriores:

Este exemplo declara uma constante chamada hd e atribuí a ela uma instância de Resolution inicializada com a largura e altura de um video full HD (1920 pixels de largura por 1080 pixels de altura).

O exemplo então declara uma variável chamada hd e atribui a ela o valor de hd. Como Resolution é uma structure, uma cópia da instância existente é feita e atribuída para a variável cinema. Mesmo que agora hd e cinema tenham a mesma altura e largura, eles são duas instâncias completamente diferentes por trás dos panos.

Abaixo, o valor da propriedade width de cinema é alterada para ser a largura de um monitor 2k padrão usado para projeção em cinemas (2048 pixels de largura por 1080 de altura):

Verificando a propriedade width de cinema, vemos que ela realmente mudou para 2048:

Entretando, a propriedade width da instância original hd ainda continua com o seu valor antigo de 1920:

Quando cinema recebeu o valor de hd, os valores armazenados em hd foram copiados como uma nova instância para cinema. O resultado final é duas instâncias completamente separadas, que possuem os mesmos valores númericos (pois foram copiadas). Como são instâncias separadas, setar a propriedade width de cinema para 2048 não afeta a valor da propriedade width armazenada em hd.

O mesmo comportamento se aplica para enumerations:

Quando rememberedDirection recebe o valor de currentDirection, ele está recebendo uma cópia daquele valor na verdade. Mudar o valor de currentDirection depois disso, não afeta a cópia do valor original que foi armazenado em rememberedDirection.

Classes são tipos de referência

Diferente dos tipos de valor, os tipos de referência não são copiados quando são atribuídos para uma variável ou constante, ou quando são passados para uma função. Ao invés de uma cópia, uma referência para a mesma instância existente é usada no lugar.

Aqui temos um exemplo, usando a classe VideoMode definida nos exemplos acima:

Este exemplo declara uma nova constante chamada tenEighty e seta para referenciar uma nova instância da classe VideoMode. Então a propriedade resolution recebe uma cópia da resolução HD de 1920 por 1080 armazenada em hd definida anteriormente. Sua prorpriedade interlaced é setada para true juntamente com a propriedade name para "1080i", e por fim é setado o valor de 25.0 para sua propriedade frameRate para simbolizar os frames por segundo.

Abaixo, tenEighty é atribuído para uma nova constante chamada alsoTenEighty, e a sua propriedade frameRate é modificada:

Como classes são tipos de referência, tenEighty e alsoTenEighty na verdade fazem referência para a mesma instância da classe VideoMode. Efetivamente eles são só dois nomes diferentes que apontam para a mesma instância única.

Verificando a propriedade frameRate de tenEighty, vemos que ela mostra corretamente o novo valor para frameRate de 30.0 que foi modificado pela constante alsoTenEighty:

Note que tenEighty e alsoTenEighty são declarados como constantes ao invés de variáveis. Entretanto você ainda pode mudar as propriedades tenEighty.frameRate e alsoTenEighty.frameRate porque os valores de tenEighty e alsoTenEighty em si não estão mudando. Eles não "armazenam" uma instância de VideoMode, na verdade eles fazem referência (apontam para uma parte na memória) para uma instância de VideoMode por trás dos panos. É o valor da propriedade frameRate dentro de VideoMode que está mudando e não as referências.

Operadores de identidade

Como classes são tipos de referência, é possível que várias constantes e variáveis referenciem a mesma instância de uma classe por trás dos panos. (O mesmo não é válido para structures e enumerations, porque eles sempre são copiados quando são atribuídos para uma constante ou variável ou passado para uma função.)
Em algumas situações pode ser útil descobrir se duas constantes ou variáveis fazem referência para exatamente a mesma instância de uma classe. Para fazer isso, o Swift fornece dois operadores de identidade:

  • Idêntico a (===)
  • Não idêntico a (!==)

Use esses operadores para verificar se duas constantes ou variáveis fazem referência para a mesma instância:

Note que o operador "identico a" (representado por 3 sinais de igual, ou ===) não significa o mesmo que "igual a" (representado por 2 sinais de igual, ou ==):

  • === significa que duas constantes ou variáveis de um tipo de uma classe fazem referência para exatamente a mesma instância.
  • == Significa que duas instâncias são consideradas "iguais" ou "equivalentes" em valor, para algum significado apropriado para "igual" que é definido pelo criador do tipo.

Quando você define sua própria classe ou structure customizada, é sua responsabilidade decidir como duas instâncias são consideradas "iguais". O processo de definir suas implementações de "igual a" ou "não igual a" é descrito na seção Operadores Avançados.

Ponteiros

Se você tem experiência com C, C++ ou Objective-C, você deve saber que essas linguagens usam ponteiros para referenciar endereços na memória. Uma variável ou constante no Swift que faz referência a uma instância de algum tipo de referência é similar a um ponteiro em C, mas não é um ponteiro direto para um endereço na memória, e não exige que você escreva um asterisco * para indicar que você está criando uma referência. Ao invés disso, elas são definidas como qualquer outra constante ou variável no Swift.

Escolhendo entre Classes e Structures

Você pode usar tanto classes quanto structures para definir seus tipos customizados para serem usados como blocos construtores do código do seu programa.
Entretanto, instâncias de structures sempre são passadas por valor e instâncias de classes sempre são passadas por referência. Isso significa que elas são adequadas para tipos diferentes de tarefas. À medida que você considera como serão seus tipos e as funcionalidades que você precisa para seu projeto, decida como cada uma delas deve ser definada em seu projeto como classe ou structure.

Como um guia geral, considere criar uma structure quando uma ou mais dessas condições se aplicarem:

  • O objetivo principal de uma structure é encapsular alguns valores de dados relativamente simples.

  • É razoável esperar que os valores encapsulados sejam copiados e não referenciados quando você atribui ou passa uma instância dessa structure.

  • Todas as propriedades armazenadas por essa structure também são tipos de valor e é esperado que também sejam copiadas ao invés de referenciadas.

  • A structure não precisa herdar propriedades ou comportamentos de outro tipo existente.

Exemplos de bons candidatos para structures incluem:

  • O tamanho geométrico de uma forma, talvez encapsulando as propriedades width e height , ambos do tipo Double.
  • Uma maneira de referenciar um range dentro de uma série, talvez encapsulando as propriedades start e length, ambos do tipo Int.
  • Um ponto em um sistema de coordenada 3D, talvez encapsulando as propriedades x, y e z, todas do tipo Double.

Em todos os outros casos, defina uma classe e crie instâncias dessa classe para ser gerenciada e passada por referência. Na pratica, isso signifca que a maioria dos seus tipos customizados devem ser classes, não structures.

Comportamento de Atribuição e Cópia para Strings, Arrays e Dictionaries

No Swift, muitos dos seus tipos básicos como String, Array e Dictionary são implementados como structures. Isso significa que dados desses tipos são copiados quando eles são atribuídos para uma nova constante ou variável ou quando são passados para uma função ou método.

Este comportamento é diferente para as classes do framework Foundation: NSString, NSArray e NSDictionary são implementados como classes e não structures, portanto eles sempre são passados como referência quando são atribuídos para uma nova constante ou variável, ao invés de serem copiados.

Nota

A descrição acima se refere à cópia de strings, arrays e dictionaries. O comportamento que você verá no seu código é sempre como se uma cópia tivesse sendo executada. Entretanto, o Swift só executa uma verdadeira cópia por trás dos panos quando é realmente necessário. Ele controla todo o processo de cópia para garantir a otimização de performance, e você não deve evitar atribuições para tentar evitar essa otimização.

results matching ""

    No results matching ""