Swift - Guia rápido

Swift 4 é uma nova linguagem de programação desenvolvida pela Apple Inc para iOS e desenvolvimento OS X. O Swift 4 adota o melhor de C e Objective-C, sem as restrições de compatibilidade de C.

  • O Swift 4 faz uso de padrões de programação seguros.

  • O Swift 4 oferece recursos de programação modernos.

  • Swift 4 fornece sintaxe semelhante a Objective-C.

  • Swift 4 é uma maneira fantástica de escrever aplicativos para iOS e OS X.

  • O Swift 4 fornece acesso perfeito às estruturas Cocoa existentes.

  • O Swift 4 unifica as partes procedurais e orientadas a objetos da linguagem.

  • O Swift 4 não precisa de uma importação de biblioteca separada para oferecer suporte a funcionalidades como entrada / saída ou manipulação de strings.

O Swift 4 usa o mesmo tempo de execução do sistema Obj-C existente no Mac OS e iOS, o que permite que os programas do Swift 4 sejam executados em muitas plataformas iOS 6 e OS X 10.8 existentes.

O Swift 4 vem com um recurso de playground onde os programadores do Swift 4 podem escrever seu código e executá-lo para ver os resultados imediatamente.

O primeiro lançamento público do Swift foi lançado em 2010. Demorou Chris Lattnerquase 14 anos para surgir com a primeira versão oficial e, mais tarde, ela foi apoiada por muitos outros contribuidores. Swift 4 foi incluído no Xcode 6 beta.

Os designers do Swift tiraram ideias de várias outras linguagens populares, como Objective-C, Rust, Haskell, Ruby, Python, C # e CLU.

Configuração de ambiente local

O Swift 4 fornece uma plataforma Playground para fins de aprendizagem e vamos configurar a mesma. Você precisa do software xCode para iniciar a codificação do Swift 4 no Playground. Assim que estiver familiarizado com os conceitos do Swift 4, você pode usar o xCode IDE para o desenvolvimento de aplicativos iOS / OS x.

Para começar, consideramos que você já tem uma conta no site Apple Developer. Depois de fazer login, vá para o seguinte link - Download para Desenvolvedores Apple

Isso listará uma série de softwares disponíveis da seguinte forma -

Agora selecione xCode e baixe-o clicando no link fornecido próximo à imagem do disco. Depois de baixar o arquivo dmg, você pode instalá-lo simplesmente clicando duas vezes nele e seguindo as instruções fornecidas. Finalmente, siga as instruções fornecidas e solte o ícone xCode na pasta do aplicativo.

Agora você tem o xCode instalado em sua máquina. Em seguida, abra o Xcode na pasta Aplicativo e prossiga após aceitar os termos e condições. Se tudo estiver bem, você verá a seguinte tela -

Selecione Get started with a playgroundopção e insira um nome para playground e selecione iOS como plataforma. Finalmente, você obterá a janela Playground da seguinte forma -

A seguir está o código obtido da janela padrão do Swift 4 Playground.

import UIKit
var str = "Hello, playground"

Se você criar o mesmo programa para o programa OS X, ele incluirá a importação de Cocoa e o programa terá a seguinte aparência -

import Cocoa
var str = "Hello, playground"

Quando o programa acima é carregado, ele deve exibir o seguinte resultado na área de resultados Playground (lado direito).

Hello, playground

Parabéns, você tem seu ambiente de programação Swift 4 pronto e pode prosseguir com seu veículo de aprendizagem "Tutorials Point".

Já vimos um pedaço do programa Swift 4 durante a configuração do ambiente. Vamos começar mais uma vez com o seguinteHello, World! programa criado para o playground do OS X, que inclui import Cocoa como mostrado abaixo -

/* My first program in Swift 4 */
var myString = "Hello, World!"

print(myString)

Se você criar o mesmo programa para o playground do iOS, ele incluirá import UIKit e o programa será o seguinte -

import UIKit
var myString = "Hello, World!"
print(myString)

Quando executamos o programa acima usando um playground apropriado, obteremos o seguinte resultado -

Hello, World!

Vamos agora ver a estrutura básica de um programa Swift 4, para que seja fácil para você entender os blocos de construção básicos da linguagem de programação Swift 4.

Importar em Swift 4

Você pode usar o importdeclaração para importar qualquer estrutura Objective-C (ou biblioteca C) diretamente em seu programa Swift 4. Por exemplo, o acimaimport cocoa declaração torna todas as bibliotecas Cocoa, APIs e tempos de execução que formam a camada de desenvolvimento para todo o OS X, disponíveis no Swift 4.

Cocoa é implementado em Objective-C, que é um superconjunto de C, então é fácil misturar C e até C ++ em seus aplicativos Swift 4.

Tokens em Swift 4

Um programa Swift 4 consiste em vários tokens e um token é uma palavra-chave, um identificador, uma constante, um literal de string ou um símbolo. Por exemplo, a seguinte instrução Swift 4 consiste em três tokens -

print("test!")
The individual tokens are:
print("test!")

Comentários

Os comentários são como textos de ajuda em seu programa Swift 4. Eles são ignorados pelo compilador. Os comentários de várias linhas começam com / * e terminam com os caracteres * / conforme mostrado abaixo -

/* My first program in Swift 4 */

Comentários multilinhas podem ser aninhados no Swift 4. A seguir está um comentário válido no Swift 4 -

/* My first program in Swift 4 is Hello, World!
/* Where as second program is Hello, Swift 4! */ */

Comentários de uma linha são escritos usando // no início do comentário.

// My first program in Swift 4

Ponto e vírgula

O Swift 4 não requer que você digite um ponto e vírgula (;) após cada instrução em seu código, embora seja opcional; e se você usar um ponto-e-vírgula, o compilador não reclamará disso.

No entanto, se você estiver usando várias instruções na mesma linha, é necessário usar um ponto-e-vírgula como delimitador, caso contrário, o compilador gerará um erro de sintaxe. Você pode escrever o texto acima Hello, World! programa da seguinte forma -

/* My first program in Swift 4 */
var myString = "Hello, World!"; print(myString)

Identificadores

Um identificador Swift 4 é um nome usado para identificar uma variável, função ou qualquer outro item definido pelo usuário. Um identificador começa com um alfabeto de A a Z ou a a z ou um sublinhado _ seguido por zero ou mais letras, sublinhados e dígitos (0 a 9).

O Swift 4 não permite caracteres especiais como @, $ e% nos identificadores. Swift 4 é umcase sensitivelinguagem de programação. Assim, Manpower e manpower são dois identificadores diferentes no Swift 4. Aqui estão alguns exemplos de identificadores aceitáveis ​​-

Azad        zara   abc   move_name   a_123
myname50    _temp  j     a23b9       retVal

Para usar uma palavra reservada como identificador, você precisará colocar um crase (`) antes e depois dela. Por exemplo,class não é um identificador válido, mas `class`é válido.

Palavras-chave

As seguintes palavras-chave são reservadas no Swift 4. Essas palavras reservadas não podem ser usadas como constantes ou variáveis ​​ou quaisquer outros nomes de identificador, a menos que sejam escapados com crases -

Palavras-chave usadas em declarações

Classe deinit Enum extensão
Func importar Iniciar interno
Deixei operador privado protocolo
público estático estrutura subscrito
Typealias var

Palavras-chave usadas em declarações

pausa caso continuar padrão
Faz outro Cair em para
E se dentro Retorna interruptor
Onde enquanto

Palavras-chave usadas em expressões e tipos

Como dynamicType falso é
nada auto Auto super
verdadeiro _COLUNA_ _ARQUIVO_ _FUNÇÃO_
_LINHA_

Palavras-chave usadas em contextos particulares

associatividade conveniência dinâmico didSet
final pegue infixo entrada
preguiçoso esquerda mutante Nenhum
não mutante opcional sobrepor postfix
precedência prefixo Protocolo requeridos
direito conjunto Tipo sem dono
fraco ajustarei

Espaços em branco

Uma linha contendo apenas espaços em branco, possivelmente com um comentário, é conhecida como uma linha em branco e um compilador Swift 4 a ignora totalmente.

Espaço em branco é o termo usado no Swift 4 para descrever espaços em branco, tabulações, caracteres de nova linha e comentários. Espaços em branco separam uma parte de uma instrução de outra e permitem que o compilador identifique onde um elemento em uma instrução, como int, termina e o próximo elemento começa. Portanto, na seguinte declaração -

var age

Deve haver pelo menos um caractere de espaço em branco (geralmente um espaço) entre var e agepara que o compilador seja capaz de distingui-los. Por outro lado, na seguinte declaração -

int fruit = apples + oranges   //get the total fruits

Nenhum caractere de espaço em branco é necessário entre fruta e =, ou entre = e maçãs, embora você seja livre para incluir alguns para melhor legibilidade.

O espaço em ambos os lados de um operador deve ser igual, por exemplo.

int fruit = apples +oranges    //is a wrong statement
int fruit = apples + oranges   //is a Correct statement

Literais

Um literal é a representação do código-fonte de um valor de um número inteiro, número de ponto flutuante ou tipo de string. A seguir estão exemplos de literais -

92               // Integer literal
4.24159          // Floating-point literal
"Hello, World!"  // String literal

Imprimindo em Swift

Para imprimir qualquer coisa rapidamente, temos a palavra-chave 'imprimir'.

A impressão tem três propriedades diferentes.

Items - Itens a serem impressos

Separator - separador entre itens

Terminator - o valor com o qual a linha deve terminar, vamos ver um exemplo e a sintaxe do mesmo.

print("Items to print", separator: "Value " , terminator: "Value")
// E.g. of print statement.

print("Value one")
// prints "Value one \n" Adds, \n as terminator and " " as separator by
default.

print("Value one","Value two", separator: " Next Value" , terminator: " End")
//prints "Value one Next Value Value two End"

No código acima, a primeira instrução de impressão adiciona \ n, feed de nova linha como terminador por padrão, onde, como na segunda instrução de impressão, fornecemos "End" como terminador, portanto, ele imprimirá "End" em vez de \ n.

Podemos fornecer nosso separador e terminadores personalizados de acordo com nossas necessidades.

Ao programar em qualquer linguagem de programação, você precisa usar diferentes tipos de variáveis ​​para armazenar informações. As variáveis ​​nada mais são do que locais de memória reservados para armazenar valores. Isso significa que, ao criar uma variável, você reserva algum espaço na memória.

Você pode gostar de armazenar informações de vários tipos de dados como string, caractere, caractere largo, inteiro, ponto flutuante, Booleano, etc. Com base no tipo de dados de uma variável, o sistema operacional aloca memória e decide o que pode ser armazenado no reservado memória.

Tipos de dados integrados

O Swift 4 oferece ao programador uma rica variedade de tipos de dados integrados e definidos pelo usuário. Os seguintes tipos de tipos de dados básicos são mais frequentes ao declarar variáveis ​​-

  • Int or UInt- Isso é usado para números inteiros. Mais especificamente, você pode usar Int32, Int64 para definir um inteiro assinado de 32 ou 64 bits, enquanto UInt32 ou UInt64 para definir variáveis ​​inteiras sem sinal de 32 ou 64 bits. Por exemplo, 42 e -23.

  • Float- É usado para representar um número de ponto flutuante de 32 bits e números com casas decimais menores. Por exemplo, 3,14159, 0,1 e -273,158.

  • Double- Isso é usado para representar um número de ponto flutuante de 64 bits e usado quando os valores de ponto flutuante devem ser muito grandes. Por exemplo, 3,14159, 0,1 e -273,158.

  • Bool - Isso representa um valor booleano que é verdadeiro ou falso.

  • String- Esta é uma coleção ordenada de personagens. Por exemplo, "Hello, World!"

  • Character- Este é um literal de string de um único caractere. Por exemplo, "C"

  • Optional - Isso representa uma variável que pode conter um valor ou nenhum valor.

  • Tuples - Isso é usado para agrupar vários valores em um único valor composto.

Listamos aqui alguns pontos importantes relacionados aos tipos inteiros -

  • Em uma plataforma de 32 bits, Int é do mesmo tamanho que Int32.

  • Em uma plataforma de 64 bits, Int tem o mesmo tamanho que Int64.

  • Em uma plataforma de 32 bits, UInt tem o mesmo tamanho que UInt32.

  • Em uma plataforma de 64 bits, UInt tem o mesmo tamanho que UInt64.

  • Int8, Int16, Int32, Int64 podem ser usados ​​para representar formas de inteiro assinado de 8 bits, 16 bits, 32 bits e 64 bits.

  • UInt8, UInt16, UInt32 e UInt64 podem ser usados ​​para representar formas de inteiros sem sinal de 8 bits, 16 bits, 32 bits e 64 bits.

Valores Vinculados

A tabela a seguir mostra o tipo de variável, quanta memória é necessária para armazenar o valor na memória e quais são os valores máximo e mínimo que podem ser armazenados neste tipo de variáveis.

Tipo Largura de bit típica Alcance Típico
Int8 1 byte -127 a 127
UInt8 1 byte 0 a 255
Int32 4 bytes -2147483648 a 2147483647
UInt32 4 bytes 0 a 4294967295
Int64 8 bytes -9223372036854775808 a 9223372036854775807
UInt64 8 bytes 0 a 18446744073709551615
Flutuador 4 bytes 1.2E-38 a 3.4E + 38 (~ 6 dígitos)
em dobro 8 bytes 2,3E-308 a 1,7E + 308 (~ 15 dígitos)

Digite aliases

Você pode criar um novo nome para um tipo existente usando typealias. Aqui está a sintaxe simples para definir um novo tipo usando typealias -

typealias newname = type

Por exemplo, a linha a seguir instrui o compilador que Feet é outro nome para Int -

typealias Feet = Int

Agora, a seguinte declaração é perfeitamente legal e cria uma variável inteira chamada distância -

typealias Feet = Int
var distance: Feet = 100
print(distance)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado.

100

Segurança de tipo

Swift 4 é uma linguagem de tipo seguro, o que significa que se uma parte do seu código espera uma String, você não pode passar um Int por engano.

Como o Swift 4 é seguro para tipos, ele executa verificações de tipo ao compilar seu código e sinaliza quaisquer tipos incompatíveis como erros.

var varA = 42
varA = "This is hello"
print(varA)

Quando compilamos o programa acima, ele produz o seguinte erro de tempo de compilação.

main.swift:2:8: error: cannot assign value of type 'String' to type 'Int'
varA = "This is hello"

Inferência de tipo

A inferência de tipo permite que um compilador deduza o tipo de uma expressão específica automaticamente ao compilar seu código, simplesmente examinando os valores fornecidos. O Swift 4 usa inferência de tipo para descobrir o tipo apropriado como segue.

// varA is inferred to be of type Int
var varA = 42
print(varA)

// varB is inferred to be of type Double
var varB = 3.14159
print(varB)

// varC is also inferred to be of type Double
var varC = 3 + 0.14159
print(varC)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

42
3.14159
3.14159

Uma variável nos fornece armazenamento nomeado que nossos programas podem manipular. Cada variável no Swift 4 possui um tipo específico, que determina o tamanho e o layout da memória da variável; a faixa de valores que podem ser armazenados nessa memória; e o conjunto de operações que podem ser aplicadas à variável.

Swift 4 suporta os seguintes tipos básicos de variáveis ​​-

  • Int or UInt- Isso é usado para números inteiros. Mais especificamente, você pode usar Int32, Int64 para definir um inteiro assinado de 32 ou 64 bits, enquanto UInt32 ou UInt64 para definir variáveis ​​inteiras sem sinal de 32 ou 64 bits. Por exemplo, 42 e -23.

  • Float- Isso é usado para representar um número de ponto flutuante de 32 bits. É usado para armazenar números com casas decimais menores. Por exemplo, 3,14159, 0,1 e -273,158.

  • Double- Isso é usado para representar um número de ponto flutuante de 64 bits e usado quando os valores de ponto flutuante devem ser muito grandes. Por exemplo, 3,14159, 0,1 e -273,158.

  • Bool - Isso representa um valor booleano que é verdadeiro ou falso.

  • String- Esta é uma coleção ordenada de personagens. Por exemplo, "Hello, World!"

  • Character- Este é um literal de string de um único caractere. Por exemplo, "C"

O Swift 4 também permite definir vários outros tipos de variáveis, que abordaremos nos capítulos subsequentes, como Optional, Array, Dictionaries, Structures, e Classes.

A seção a seguir cobrirá como declarar e usar vários tipos de variáveis ​​na programação do Swift 4.

Declaração de Variável

Uma declaração de variável informa ao compilador onde e quanto criar o armazenamento para a variável. Antes de usar variáveis, você deve declará-las usandovar palavra-chave da seguinte forma -

var variableName = <initial value>

O exemplo a seguir mostra como declarar uma variável em Swift 4 -

var varA = 42
print(varA)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

42

Anotações de tipo

Você pode fornecer um type annotationquando você declara uma variável, para ser claro sobre o tipo de valores que a variável pode armazenar. Aqui está a sintaxe -

var variableName:<data type> = <optional initial value>

O exemplo a seguir mostra como declarar uma variável no Swift 4 usando Anotação. Aqui é importante notar que se não estivermos usando anotação de tipo, então torna-se obrigatório fornecer um valor inicial para a variável, caso contrário, podemos apenas declarar nossa variável usando a anotação de tipo.

var varA = 42
print(varA)

var varB:Float

varB = 3.14159
print(varB)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

42
3.1415901184082

Nomeando Variáveis

O nome de uma variável pode ser composto de letras, dígitos e o caractere de sublinhado. Deve começar com uma letra ou um sublinhado. As letras maiúsculas e minúsculas são distintas porque o Swift 4 é uma linguagem de programação que diferencia maiúsculas de minúsculas.

Você pode usar caracteres simples ou Unicode para nomear suas variáveis. Os exemplos a seguir mostram como você pode nomear as variáveis ​​-

var _var = "Hello, Swift 4!"
print(_var)

var 你好 = "你好世界"
print(你好)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado.

Hello, Swift 4!
你好世界

Variáveis ​​de impressão

Você pode imprimir o valor atual de uma constante ou variável com a função de impressão. Você pode interpolar um valor de variável envolvendo o nome entre parênteses e escapá-lo com uma barra invertida antes do parêntese de abertura: A seguir estão os exemplos válidos -

var varA = "Godzilla"
var varB = 1000.00

print("Value of \(varA) is more than \(varB) millions")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado.

Value of Godzilla is more than 1000.0 millions

Swift 4 também apresenta Optionalstipo, que lida com a ausência de um valor. Os opcionais dizem "há um valor e é igual a x" ou "não há valor algum".

Um opcional é um tipo por si só, na verdade um dos novos enums superpoderosos do Swift 4. Tem dois valores possíveis,None e Some(T), Onde T é um valor associado do tipo de dados correto disponível no Swift 4.

Aqui está uma declaração opcional de inteiro -

var perhapsInt: Int?

Aqui está uma declaração String opcional -

var perhapsStr: String?

A declaração acima é equivalente a explicitamente inicializá-lo para nil o que significa nenhum valor -

var perhapsStr: String? = nil

Vamos dar o seguinte exemplo para entender como funcionam os opcionais no Swift 4 -

var myString:String? = nil

if myString != nil {
   print(myString)
} else {
   print("myString has nil value")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

myString has nil value

Opcionais são semelhantes a usar nil com ponteiros em Objective-C, mas funcionam para qualquer tipo, não apenas classes.

Desembrulhar Forçado

Se você definiu uma variável como optional, então, para obter o valor desta variável, você terá que unwrapisto. Isso significa apenas colocar um ponto de exclamação no final da variável.

Vamos dar um exemplo simples -

var myString:String?

myString = "Hello, Swift 4!"

if myString != nil {
   print(myString)
} else {
   print("myString has nil value")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Optional("Hello, Swift 4!")

Agora vamos aplicar o desempacotamento para obter o valor correto da variável -

var myString:String?

myString = "Hello, Swift 4!"

if myString != nil {
   print( myString! )
} else {
   print("myString has nil value")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado.

Hello, Swift 4!

Desempacotamento Automático

Você pode declarar variáveis ​​opcionais usando um ponto de exclamação em vez de um ponto de interrogação. Essas variáveis ​​opcionais serão desdobradas automaticamente e você não precisa usar mais nenhum ponto de exclamação no final da variável para obter o valor atribuído. Vamos dar um exemplo simples -

var myString:String!
myString = "Hello, Swift 4!"

if myString != nil {
   print(myString)
} else {
   print("myString has nil value")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Hello, Swift 4!

Ligação opcional

Use a ligação opcional para descobrir se um opcional contém um valor e, em caso afirmativo, para disponibilizar esse valor como uma constante ou variável temporária.

Uma ligação opcional para o if declaração é a seguinte -

if let constantName = someOptional {
   statements
}

Vamos dar um exemplo simples para entender o uso da ligação opcional -

var myString:String?
myString = "Hello, Swift 4!"

if let yourString = myString {
   print("Your string has - \(yourString)")
} else {
   print("Your string does not have a value")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Your string has - Hello, Swift 4!

Swift 4 também apresenta Tuples tipo, que são usados ​​para agrupar vários valores em um único valor composto.

Os valores em uma tupla podem ser de qualquer tipo e não precisam ser do mesmo tipo.

Por exemplo, ("Tutorials Point", 123) é uma tupla com dois valores, um do tipo string e outro do tipo inteiro. É um comando legal.

let ImplementationError = (501, "Não implementado") é um erro quando algo no servidor não está implementado, ele retorna dois valores. Código de erro e descrição.

Você pode criar tuplas de quantos valores desejar e de qualquer número de tipos de dados diferentes.

Aqui está a sintaxe da declaração de Tupla -

var TupleName = (Value1, value2,… any number of values)

Aqui está uma declaração de Tupla -

var error501 = (501, “Not implemented”)

Você pode acessar os valores da tupla usando os números do índice que começam em 0.

Aqui está um exemplo de acesso a valores de tupla -

print(“The code is\(error501.0)”)
print(“The definition of error is\(error501.1)”)

Você pode nomear as variáveis ​​de uma tupla durante a declaração e pode chamá-las usando seus nomes

var error501 = (errorCode: 501, description: “Not Implemented”)
print(error501.errorCode)   // prints 501.

As tuplas são úteis para retornar vários valores de uma função. Da mesma forma, um aplicativo da web pode retornar uma tupla do tipo ("String", Int) para mostrar se o carregamento foi bem-sucedido ou falhou.

Ao retornar valores diferentes em uma tupla, podemos tomar decisões dependendo dos diferentes tipos de tupla.

Note - Tuplas são úteis para valores temporários e não são adequadas para dados complexos.

Constantes referem-se a valores fixos que um programa não pode alterar durante sua execução. As constantes podem ser de qualquer um dos tipos de dados básicos, como uma constante inteira, uma constante flutuante, uma constante de caractere ou um literal de string . Também existem constantes de enumeração .

Constants são tratados como variáveis ​​regulares, exceto pelo fato de que seus valores não podem ser modificados após sua definição.

Declaração de Constantes

Antes de usar constantes, você deve declará-las usando let palavra-chave da seguinte forma -

let constantName = <initial value>

A seguir está um exemplo simples para mostrar como declarar uma constante no Swift 4 -

let constA = 42
print(constA)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

42

Anotações de tipo

Você pode fornecer um type annotationquando você declara uma constante, para ser claro sobre o tipo de valores que a constante pode armazenar. A seguir está a sintaxe -

var constantName:<data type> = <optional initial value>

O exemplo a seguir mostra como declarar uma constante no Swift 4 usando Anotação. Aqui é importante notar que é obrigatório fornecer um valor inicial ao criar uma constante -

let constA = 42
print(constA)

let constB:Float = 3.14159
print(constB)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado.

42
3.1415901184082

Naming Constants

O nome de uma constante pode ser composto de letras, dígitos e o caractere de sublinhado. Deve começar com uma letra ou um sublinhado. As letras maiúsculas e minúsculas são distintas porque o Swift 4 é uma linguagem de programação que diferencia maiúsculas de minúsculas.

Você pode usar caracteres simples ou Unicode para nomear suas variáveis. A seguir estão exemplos válidos -

let _const = "Hello, Swift 4!"
print(_const)

let 你好 = "你好世界"
print(你好)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Hello, Swift 4!
你好世界

Imprimindo constantes

Você pode imprimir o valor atual de uma constante ou variável usando printfunção. Você pode interpolar um valor de variável envolvendo o nome entre parênteses e escapá-lo com uma barra invertida antes do parêntese de abertura: A seguir estão os exemplos válidos -

let constA = "Godzilla"
let constB = 1000.00

print("Value of \(constA) is more than \(constB) millions")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Value of Godzilla is more than 1000.0 millions

Um literal é a representação do código-fonte de um valor de um número inteiro, número de ponto flutuante ou tipo de string. A seguir estão exemplos de literais -

42                // Integer literal
3.14159           // Floating-point literal
"Hello, world!"   // String literal

Literais inteiros

Um literal inteiro pode ser uma constante decimal, binária, octal ou hexadecimal. Literais binários começam com 0b, literais octais começam com 0o e literais hexadecimais começam com 0x e nada para decimal.

Aqui estão alguns exemplos de literais inteiros -

let decimalInteger = 17         // 17 in decimal notation
let binaryInteger = 0b10001     // 17 in binary notation
let octalInteger = 0o21         // 17 in octal notation
let hexadecimalInteger = 0x11   // 17 in hexadecimal notation

Literais de ponto flutuante

Um literal de ponto flutuante possui uma parte inteira, um ponto decimal, uma parte fracionária e uma parte expoente. Você pode representar literais de ponto flutuante na forma decimal ou hexadecimal.

Literais de ponto flutuante decimal consistem em uma sequência de dígitos decimais seguidos por uma fração decimal, um expoente decimal ou ambos.

Literais de ponto flutuante hexadecimal consistem em um prefixo 0x, seguido por uma fração hexadecimal opcional, seguida por um expoente hexadecimal.

Aqui estão alguns exemplos de literais de ponto flutuante -

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

Literais de string

Um literal de string é uma sequência de caracteres entre aspas duplas, com a seguinte forma -

"characters"

Literais de string não podem conter aspas duplas sem escape ("), barra invertida sem escape (\), retorno de carro ou alimentação de linha. Caracteres especiais podem ser incluídos em literais de string usando as seguintes sequências de escape -

Sequência de fuga Significado
\ 0 Caráter Nulo
\\ \personagem
\ b Backspace
\ f Feed de formulário
\ n Nova linha
\ r Retorno de carruagem
\ t Aba horizontal
\ v Aba vertical
\ ' Citação única
\ " Citação dupla
\ 000 Número octal de um a três dígitos
\ xhh ... Número hexadecimal de um ou mais dígitos

O exemplo a seguir mostra como usar alguns literais de string -

let stringL = "Hello\tWorld\n\nHello\'Swift 4\'"
print(stringL)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Hello World

Hello'Swift 4'

Literais booleanos

Existem três literais booleanos e eles fazem parte das palavras-chave padrão do Swift 4 -

  • Um valor de true representando verdadeiro.

  • Um valor de false representando falso.

  • Um valor de nil representando nenhum valor.

Um operador é um símbolo que informa ao compilador para executar manipulações matemáticas ou lógicas específicas. Objective-C é rico em operadores integrados e fornece os seguintes tipos de operadores -

  • Operadores aritméticos
  • Operadores de comparação
  • Operadores lógicos
  • Operadores bit a bit
  • Operadores de atribuição
  • Operadores de alcance
  • Operadores diversos

Este tutorial irá explicar os operadores aritméticos, relacionais, lógicos, bit a bit, atribuição e outros, um por um.

Operadores aritméticos

A tabela a seguir mostra todos os operadores aritméticos suportados pela linguagem Swift 4. Assumir variávelA contém 10 e variável B contém 20, então -

Operador Descrição Exemplo
+ Adiciona dois operandos A + B dará 30
- Subtrai o segundo operando do primeiro A - B dará -10
* Multiplica ambos os operandos A * B dará 200
/ Divide numerador por denominador B / A dará 2
% Operador de Módulo e o restante após uma divisão inteiro / flutuante B% A dará 0

Operadores de comparação

A tabela a seguir mostra todos os operadores relacionais suportados pela linguagem Swift 4. Assumir variávelA contém 10 e variável B contém 20, então -

Operador Descrição Exemplo
== Verifica se os valores de dois operandos são iguais ou não; se sim, então a condição se torna verdadeira. (A == B) não é verdade.
! = Verifica se os valores de dois operandos são iguais ou não; se os valores não forem iguais, a condição se torna verdadeira. (A! = B) é verdade.
> Verifica se o valor do operando esquerdo é maior que o valor do operando direito; se sim, então a condição se torna verdadeira. (A> B) não é verdade.
< Verifica se o valor do operando esquerdo é menor que o valor do operando direito; se sim, então a condição se torna verdadeira. (A <B) é verdade.
> = Verifica se o valor do operando esquerdo é maior ou igual ao valor do operando direito; se sim, então a condição se torna verdadeira. (A> = B) não é verdade.
<= Verifica se o valor do operando esquerdo é menor ou igual ao valor do operando direito; se sim, então a condição se torna verdadeira. (A <= B) é verdadeiro.

Operadores lógicos

A tabela a seguir mostra todos os operadores lógicos suportados pela linguagem Swift 4. Assumir variávelA detém 1 e variável B segura 0, então -

Operador Descrição Exemplo
&& Operador lógico chamado AND. Se ambos os operandos forem diferentes de zero, a condição se torna verdadeira. (A && B) é falso.
|| Operador lógico ou chamado. Se qualquer um dos dois operandos for diferente de zero, a condição se torna verdadeira. (A || B) é verdade.
! Operador lógico chamado NOT. Use para reverter o estado lógico de seu operando. Se uma condição for verdadeira, o operador lógico NOT a tornará falsa. ! (A && B) é verdade.

Operadores bit a bit

Operadores bit a bit trabalham em bits e realizam operação bit a bit. As tabelas de verdade para &, | e ^ são as seguintes -

p q p & q p | q p ^ q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1
Assume A = 60; and B = 13;

In binary format, they will be as follows:

A = 0011 1100

B = 0000 1101

-----------------

A & B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

~A = 1100 0011

Os operadores bit a bit suportados pela linguagem Swift 4 estão listados na tabela a seguir. Assumir variávelA detém 60 e variável B contém 13, então 7−

Operador Descrição Exemplo
E O operador Binário AND copia um bit para o resultado, se existir em ambos os operandos. (A e B) dará 12, que é 0000 1100
| O operador binário OR copia um bit, se existir em qualquer operando. (A | B) dará 61, que é 0011 1101
^ O operador binário XOR copia o bit, se estiver definido em um operando, mas não em ambos. (A ^ B) dará 49, que é 0011 0001
~ O operador de complemento binários é unário e tem o efeito de 'inverter' bits. (~ A) dará -61, que é 1100 0011 na forma de complemento de 2.
<< Operador binário de deslocamento à esquerda. O valor dos operandos à esquerda é movido para a esquerda pelo número de bits especificado pelo operando à direita. (A << 2 dará 240, que é 1111 0000
>> Operador binário de deslocamento à direita. O valor dos operandos à esquerda é movido para a direita pelo número de bits especificado pelo operando à direita. Um >> 2 dará 15, que é 0000 1111

Operadores de atribuição

SSwift 4 suporta os seguintes operadores de atribuição -

Operador Descrição Exemplo
= Operador de atribuição simples, atribui valores de operandos do lado direito para operando do lado esquerdo C = A + B irá atribuir o valor de A + B em C
+ = Adicionar operador de atribuição AND, adiciona o operando direito ao operando esquerdo e atribui o resultado ao operando esquerdo C + = A é equivalente a C = C + A
- = Subtrai o operador de atribuição E, subtrai o operando direito do operando esquerdo e atribui o resultado ao operando esquerdo C - = A é equivalente a C = C - A
* = Operador de atribuição Multiply AND, multiplica o operando direito pelo operando esquerdo e atribui o resultado ao operando esquerdo C * = A é equivalente a C = C * A
/ = Operador de atribuição de divisão AND, divide o operando esquerdo com o operando direito e atribui o resultado ao operando esquerdo C / = A é equivalente a C = C / A
% = Módulo E operador de atribuição, leva o módulo usando dois operandos e atribui o resultado ao operando esquerdo C% = A é equivalente a C = C% A
<< = Deslocamento à esquerda E operador de atribuição C << = 2 é igual a C = C << 2
>> = Deslocamento à direita E operador de atribuição C >> = 2 é igual a C = C >> 2
& = Operador de atribuição AND bit a bit C & = 2 é igual a C = C & 2
^ = OR exclusivo bit a bit e operador de atribuição C ^ = 2 é igual a C = C ^ 2
| = OR inclusivo bit a bit e operador de atribuição C | = 2 é igual a C = C | 2

Operadores de alcance

O Swift 4 inclui dois operadores de intervalo, que são atalhos para expressar uma gama de valores. A tabela a seguir explica esses dois operadores.

Operador Descrição Exemplo
Faixa Fechada (a ... b) define um intervalo que vai de a até b e inclui os valores a e b. 1 ... 5 dá 1, 2, 3, 4 e 5
Alcance Meio Aberto (a .. <b) define um intervalo que vai de a até b, mas não inclui b. 1 .. <5 dá 1, 2, 3 e 4
Alcance unilateral

a ..., define um intervalo que vai de a ao final dos elementos

… A, define um intervalo começando do início a um

1 ... dá 1, 2,3 ... fim dos elementos

... 2 dá início ... a 1,2

Operadores diversos

Swift 4 suporta alguns outros operadores importantes, incluindo rangee? : que são explicados na tabela a seguir.

Operador Descrição Exemplo
Unary Minus O sinal de um valor numérico pode ser alternado usando um prefixo - -3 ou -4
Unary Plus Retorna o valor com o qual opera, sem qualquer alteração. +6 dá 6
Condicional ternária Doença ? X: Y Se a condição for verdadeira? Então valor X: Caso contrário, valor Y

Precedência de operadores

A precedência do operador determina o agrupamento de termos em uma expressão. Isso afeta como uma expressão é avaliada. Certos operadores têm precedência mais alta do que outros; por exemplo, o operador de multiplicação tem precedência mais alta do que o operador de adição.

Por exemplo, x = 7 + 3 * 2; aqui, x é atribuído a 13, não 20, porque o operador * tem precedência mais alta do que +, portanto, primeiro é multiplicado por 3 * 2 e, em seguida, é adicionado a 7.

Aqui, os operadores com a precedência mais alta aparecem na parte superior da tabela, aqueles com a mais baixa aparecem na parte inferior. Em uma expressão, os operadores de precedência superior serão avaliados primeiro.

Operador Descrição Exemplo
Operadores de expressão primária () []. expr ++ expr-- da esquerda para direita
Operadores unários

* & + -! ~ ++ expr --expr

* /%

+ -

>> <<

<> <=> =

==! =

direita para esquerda
Operadores binários

E

^

|

&&

||

da esquerda para direita
Operador Ternário ?: direita para esquerda
Operadores de atribuição = + = - = * = / =% = >> = << = & = ^ = | = direita para esquerda
Vírgula , da esquerda para direita

As estruturas de tomada de decisão requerem que o programador especifique uma ou mais condições a serem avaliadas ou testadas pelo programa, junto com uma instrução ou instruções a serem executadas se a condição for determinada como truee, opcionalmente, outras instruções a serem executadas se a condição for determinada como false.

A seguir está a parte geral de uma estrutura típica de tomada de decisão encontrada na maioria das linguagens de programação -

O Swift 4 fornece os seguintes tipos de declarações de tomada de decisão. Clique nos links a seguir para verificar seus detalhes.

Sr. Não Declaração e descrição
1 declaração if

Uma instrução if consiste em uma expressão booleana seguida por uma ou mais instruções.

2 declaração if ... else

Uma instrução if pode ser seguida por uma instrução else opcional, que é executada quando a expressão booleana é falsa.

3 if ... else if ... else Declaração

Uma instrução if pode ser seguida por uma instrução else if ... else opcional, que é muito útil para testar várias condições usando uma instrução if ... else if única.

4 declarações if aninhadas

Você pode usar uma instrução if ou else if dentro de outra instrução if ou else if.

5 declaração switch

Uma instrução switch permite que uma variável seja testada quanto à igualdade em relação a uma lista de valores.

O ? : Operador

Nós cobrimos conditional operator ? : no capítulo anterior, que pode ser usado para substituir if...elseafirmações. Tem a seguinte forma geral -

Exp1 ? Exp2 : Exp3;

Onde Exp1, Exp2 e Exp3 são expressões. Observe o uso e a localização do cólon.

O valor de um? expressão é determinada assim: Exp1 é avaliada. Se for verdade, então Exp2 é avaliado e se torna o valor de todo? expressão. Se Exp1 for falso, então Exp3 é avaliado e seu valor se torna o valor da expressão.

Pode haver uma situação em que você precise executar um bloco de código várias vezes. Em geral, as instruções são executadas sequencialmente: a primeira instrução em uma função é executada primeiro, seguida pela segunda e assim por diante.

As linguagens de programação fornecem várias estruturas de controle que permitem caminhos de execução mais complicados.

Uma instrução de loop nos permite executar uma instrução ou grupo de instruções várias vezes. A seguir está a instrução geral de um loop na maioria das linguagens de programação -

A linguagem de programação Swift 4 fornece os seguintes tipos de loop para lidar com os requisitos de loop. Clique nos links a seguir para verificar seus detalhes.

Sr. Não Tipo de Loop e Descrição
1 para dentro

Este loop executa um conjunto de instruções para cada item em um intervalo, sequência, coleção ou progressão.

2 loop while

Repete uma declaração ou grupo de declarações enquanto uma determinada condição for verdadeira. Ele testa a condição antes de executar o corpo do loop.

3 repetir ... loop while

Como uma instrução while, exceto que testa a condição no final do corpo do loop.

Declarações de controle de loop

As instruções de controle de loop alteram a execução de sua sequência normal. Quando a execução deixa um escopo, todos os objetos automáticos que foram criados nesse escopo são destruídos.

O Swift 4 suporta as seguintes instruções de controle. Clique nos links a seguir para verificar seus detalhes.

Sr. Não Declaração de controle e descrição
1 continuar declaração

Esta instrução diz a um loop para parar o que está fazendo e começar novamente no início da próxima iteração através do loop.

2 declaração de quebra

Encerra a instrução de loop e transfere a execução para a instrução imediatamente após o loop.

3 declaração de queda

A declaração de fallthrough simula o comportamento da mudança do Swift 4 para a mudança no estilo C.

Strings no Swift 4 são uma coleção ordenada de caracteres, como "Hello, World!" e eles são representados pelo tipo de dados Swift 4String, que por sua vez representa uma coleção de valores de Character tipo.

Crie uma string

Você pode criar uma String usando um literal de string ou criando uma instância de uma classe String da seguinte maneira -

// String creation using String literal
var stringA = "Hello, Swift 4!"
print( stringA )

// String creation using String instance
var stringB = String("Hello, Swift 4!")
print( stringB )

//Multiple line string

let stringC = """
Hey this is a
example of multiple Line
string by tutorialsPoint 

"""
print(stringC)

Quando o código acima é compilado e executado, ele produz o seguinte resultado

Hello, Swift 4!
Hello, Swift 4!
Hey this is a
example of multiple Line
string by tutorialsPoint

String vazia

Você pode criar um String vazio usando um literal de string vazio ou criando uma instância da classe String como mostrado abaixo. Você também pode verificar se uma string está vazia ou não usando a propriedade BooleanisEmpty.

// Empty string creation using String literal
var stringA = ""

if stringA.isEmpty {
   print( "stringA is empty" )
} else {
   print( "stringA is not empty" )
}

// Empty string creation using String instance
let stringB = String()

if stringB.isEmpty {
   print( "stringB is empty" )
} else {
   print( "stringB is not empty" )
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

stringA is empty
stringB is empty

Constantes de String

Você pode especificar se sua String pode ser modificada (ou modificada) atribuindo-a a uma variável ou se ela será constante atribuindo-a a uma constante usando let palavra-chave conforme mostrado abaixo -

// stringA can be modified
var stringA = "Hello, Swift 4!"
stringA + = "--Readers--"
print( stringA )

// stringB can not be modified
let stringB = String("Hello, Swift 4!")
stringB + = "--Readers--"
print( stringB )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Playground execution failed: error: <EXPR>:10:1: error: 'String' is not
convertible to '@lvalue UInt8'
stringB + = "--Readers--"

Interpolação de String

A interpolação de string é uma maneira de construir um novo valor de string a partir de uma mistura de constantes, variáveis, literais e expressões, incluindo seus valores dentro de um literal de string.

Cada item (variável ou constante) que você insere no literal de string é envolvido em um par de parênteses, prefixado por uma barra invertida. Aqui está um exemplo simples -

var varA = 20
let constA = 100
var varC:Float = 20.0

var stringA = "\(varA) times \(constA) is equal to \(varC * 100)"
print( stringA )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

20 times 100 is equal to 2000.0

String Concatenation

Você pode usar o operador + para concatenar duas strings ou uma string e um caractere ou dois caracteres. Aqui está um exemplo simples -

let constA = "Hello,"
let constB = "World!"

var stringA = constA + constB
print( stringA )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Hello,World!

Comprimento da corda

As cordas do Swift 4 não têm um lengthpropriedade, mas você pode usar a função global count () para contar o número de caracteres em uma string. Aqui está um exemplo simples -

var varA = "Hello, Swift 4!"

print( "\(varA), length is \((varA.count))" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Hello, Swift 4!, length is 15

Comparação de cordas

Você pode usar o operador == para comparar duas variáveis ​​ou constantes de strings. Aqui está um exemplo simples -

var varA = "Hello, Swift 4!"
var varB = "Hello, World!"

if varA == varB {
   print( "\(varA) and \(varB) are equal" )
} else {
   print( "\(varA) and \(varB) are not equal" )
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Hello, Swift 4! and Hello, World! are not equal

Iterando String

Strings são novamente uma coleção de valores no swift 4, então podemos iterar sobre string usando loops. -

for chars in "ThisString" {
   print(chars, terminator: " ")
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

T h i s S t r i n g

Strings Unicode

Você pode acessar uma representação UTF-8 e UTF-16 de uma String iterando sobre suas propriedades utf8 e utf16 conforme demonstrado no exemplo a seguir -

var unicodeString = "Dog???"

print("UTF-8 Codes: ")
for code in unicodeString.utf8 {
   print("\(code) ")
}

print("\n")

print("UTF-16 Codes: ")
for code in unicodeString.utf16 {
   print("\(code) ")
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

UTF-8 Codes: 
68 
111 
103 
63 
63 
63 


UTF-16 Codes: 
68 
111 
103 
63 
63 
63

Funções e operadores de string

O Swift 4 suporta uma ampla gama de métodos e operadores relacionados a Strings -

Sr. Não Funções / operadores e finalidade
1

isEmpty

Um valor booleano que determina se uma string está vazia ou não.

2

hasPrefix(prefix: String)

Função para verificar se uma determinada string de parâmetro existe ou não como um prefixo da string.

3

hasSuffix(suffix: String)

Função para verificar se uma determinada string de parâmetro existe ou não como sufixo da string.

4

toInt()

Função para converter o valor numérico da String em Inteiro.

5

count()

Função global para contar o número de caracteres em uma string.

6

utf8

Propriedade para retornar uma representação UTF-8 de uma string.

7

utf16

Propriedade para retornar uma representação UTF-16 de uma string.

8

unicodeScalars

Propriedade para retornar uma representação escalar Unicode de uma string.

9

+

Operador para concatenar duas strings, ou uma string e um caractere, ou dois caracteres.

10

+=

Operador para anexar uma string ou caractere a uma string existente.

11

==

Operador para determinar a igualdade de duas strings.

12

<

Operador para realizar uma comparação lexicográfica para determinar se uma string é avaliada como inferior a outra.

13

startIndex

Para obter o valor no índice inicial da string.

14

endIndex

Para obter o valor no índice final da string.

15

Indices

Para acessar os indeces um por um. ou seja, todos os caracteres da string, um por um.

16

insert("Value", at: position)

Para inserir um valor em uma posição.

17

remove(at: position)

removeSubrange(range)

para remover um valor em uma posição ou para remover um intervalo de valores da string.

18

reversed()

retorna o reverso de uma string

UMA character em Swift é um literal de string de caractere único, endereçado pelo tipo de dados Character. Dê uma olhada no exemplo a seguir. Ele usa duas constantes de caracteres -

let char1: Character = "A"
let char2: Character = "B"

print("Value of char1 \(char1)")
print("Value of char2 \(char2)")

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of char1 A
Value of char2 B

Se você tentar armazenar mais de um caractere em uma variável ou constante de tipo de caractere, o Swift 4 não permitirá isso. Tente digitar o seguinte exemplo no Swift 4 Playground e você obterá um erro antes mesmo da compilação.

// Following is wrong in Swift 4
let char: Character = "AB"

print("Value of char \(char)")

Variáveis ​​de caracteres vazias

Não é possível criar uma variável ou constante Character vazia que terá um valor vazio. A seguinte sintaxe não é possível -

// Following is wrong in Swift 4
let char1: Character = ""
var char2: Character = ""

print("Value of char1 \(char1)")
print("Value of char2 \(char2)")

Acessando Personagens de Strings

Conforme explicado ao discutir as Strings do Swift 4, String representa uma coleção de valores de caracteres em uma ordem especificada. Portanto, podemos acessar caracteres individuais de uma determinada string, iterando sobre essa string com umfor-in loop -

for ch in "Hello" {
   print(ch)
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

H
e
l
l
o

Concatenando Strings com Personagens

O exemplo a seguir demonstra como um caractere do Swift 4 pode ser concatenado com a string do Swift 4.

var varA:String = "Hello "
let varB:Character = "G"

varA.append( varB )

print("Value of varC = \(varA)")

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of varC = Hello G

Os arrays do Swift 4 são usados ​​para armazenar listas ordenadas de valores do mesmo tipo. O Swift 4 coloca uma verificação estrita que não permite que você insira um tipo errado em um array, mesmo por engano.

Se você atribuir um array criado a uma variável, ele será sempre mutável, o que significa que você pode alterá-lo adicionando, removendo ou alterando seus itens; mas se você atribuir um array a uma constante, então esse array é imutável e seu tamanho e conteúdo não podem ser alterados.

Criação de matrizes

Você pode criar uma matriz vazia de um certo tipo usando a seguinte sintaxe de inicializador -

var someArray = [SomeType]()

Aqui está a sintaxe para criar uma matriz de um determinado tamanho a * e inicializá-la com um valor -

var someArray = [SomeType](count: NumbeOfElements, repeatedValue: InitialValue)

Você pode usar a seguinte instrução para criar uma matriz vazia de Int tipo com 3 elementos e o valor inicial como zero -

var someInts = [Int](count: 3, repeatedValue: 0)

A seguir está mais um exemplo para criar uma matriz de três elementos e atribuir três valores a essa matriz -

var someInts:[Int] = [10, 20, 30]

Acessando matrizes

Você pode recuperar um valor de uma matriz usando subscript sintaxe, passando o índice do valor que você deseja recuperar entre colchetes imediatamente após o nome da matriz da seguinte maneira -

var someVar = someArray[index]

Aqui o indexcomeça em 0, o que significa que o primeiro elemento pode ser acessado usando o índice como 0, o segundo elemento pode ser acessado usando o índice como 1 e assim por diante. O exemplo a seguir mostra como criar, inicializar e acessar matrizes -

var someInts = [Int](count: 3, repeatedValue: 10)

var someVar = someInts[0]
print( "Value of first element is \(someVar)" )
print( "Value of second element is \(someInts[1])" )
print( "Value of third element is \(someInts[2])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of first element is 10
Value of second element is 10
Value of third element is 10

Modificação de matrizes

Você pode usar append()método ou operador de atribuição de adição (+ =) para adicionar um novo item no final de uma matriz. Dê uma olhada no exemplo a seguir. Aqui, inicialmente, criamos uma matriz vazia e, em seguida, adicionamos novos elementos na mesma matriz -

var someInts = [Int]()

someInts.append(20)
someInts.append(30)
someInts += [40]

var someVar = someInts[0]

print( "Value of first element is \(someVar)" )
print( "Value of second element is \(someInts[1])" )
print( "Value of third element is \(someInts[2])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of first element is 20
Value of second element is 30
Value of third element is 40

Você pode modificar um elemento existente de um Array atribuindo um novo valor em um determinado índice, conforme mostrado no exemplo a seguir -

var someInts = [Int]()

someInts.append(20)
someInts.append(30)
someInts += [40]

// Modify last element
someInts[2] = 50

var someVar = someInts[0]

print( "Value of first element is \(someVar)" )
print( "Value of second element is \(someInts[1])" )
print( "Value of third element is \(someInts[2])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of first element is 20
Value of second element is 30
Value of third element is 50

Iterando sobre uma matriz

Você pode usar for-in loop para iterar sobre todo o conjunto de valores em uma matriz, conforme mostrado no exemplo a seguir -

var someStrs = [String]()

someStrs.append("Apple")
someStrs.append("Amazon")
someStrs += ["Google"]
for item in someStrs {
   print(item)
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Apple
Amazon
Google

Você pode usar enumerate() função que retorna o índice de um item junto com seu valor, conforme mostrado abaixo no exemplo a seguir -

var someStrs = [String]()

someStrs.append("Apple")
someStrs.append("Amazon")
someStrs += ["Google"]

for (index, item) in someStrs.enumerated() {
   print("Value at index = \(index) is \(item)")
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value at index = 0 is Apple
Value at index = 1 is Amazon
Value at index = 2 is Google

Adicionando Dois Arrays

Você pode usar o operador de adição (+) para adicionar duas matrizes do mesmo tipo, o que produzirá uma nova matriz com uma combinação de valores das duas matrizes da seguinte maneira -

var intsA = [Int](count:2, repeatedValue: 2)
var intsB = [Int](count:3, repeatedValue: 1)

var intsC = intsA + intsB
for item in intsC {
   print(item)
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

2
2
1
1
1

A propriedade de contagem

Você pode usar o modo somente leitura count propriedade de uma matriz para descobrir o número de itens em uma matriz mostrada abaixo -

var intsA = [Int](count:2, repeatedValue: 2)
var intsB = [Int](count:3, repeatedValue: 1)

var intsC = intsA + intsB

print("Total items in intsA = \(intsA.count)")
print("Total items in intsB = \(intsB.count)")
print("Total items in intsC = \(intsC.count)")

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Total items in intsA = 2
Total items in intsB = 3
Total items in intsC = 5

A propriedade vazia

Você pode usar o modo somente leitura empty propriedade de uma matriz para descobrir se uma matriz está vazia ou não, conforme mostrado abaixo -

var intsA = [Int](count:2, repeatedValue: 2)
var intsB = [Int](count:3, repeatedValue: 1)
var intsC = [Int]()

print("intsA.isEmpty = \(intsA.isEmpty)")
print("intsB.isEmpty = \(intsB.isEmpty)")
print("intsC.isEmpty = \(intsC.isEmpty)")

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

intsA.isEmpty = false
intsB.isEmpty = false
intsC.isEmpty = true

Swift 4 sets são usados ​​para armazenar valores distintos dos mesmos tipos, mas eles não têm uma ordem definida como os arrays.

Você pode usar conjuntos em vez de arrays se a ordem dos elementos não for um problema ou se quiser garantir que não haja valores duplicados. (conjuntos permitem apenas valores distintos).

Um tipo deve ser hashble para ser armazenado em um conjunto. Um valor hash é um valor Int que é igual para objetos iguais. Por exemplo, se x == y, entãox.hashvalue == y.hashvalue.

Todos os valores básicos de swift são do tipo hashable por padrão e podem ser usados ​​como valores definidos.

Criação de conjuntos

Você pode criar um conjunto vazio de um certo tipo usando a seguinte sintaxe de inicializador -

var someSet = Set<Character>()     //Character can be replaced by data type of set.

Acessando e modificando conjuntos

Você pode acessar ou modificar um conjunto usando seus métodos e propriedades -

O método "contagem" pode ser usado para mostrar o número de elementos no conjunto.

someSet.count        // prints the number of elements

O método "inserir" pode ser usado para inserir valores no conjunto.

someSet.insert("c")   // adds the element to Set.

Da mesma forma, isEmpty pode ser usado para verificar se o conjunto está vazio.

someSet.isEmpty       // returns true or false depending on the set Elements.

O método "remove" pode ser usado para remover o valor no conjunto.

someSet.remove("c")     // removes a element , removeAll() can be used to remove all elements

O método "contém" pode ser usado para verificar a existência de valor em um conjunto.

someSet.contains("c")     // to check if set contains this value.

Iterando sobre um conjunto

Você pode iterar em um conjunto usando loop for-in -

for items in someSet {
   print(someSet)
}

//Swift sets are not in an ordered way, to iterate over a set in ordered way use

for items in someSet.sorted() {
   print(someSet)
}

Execução de operações de conjunto

Você pode realizar operações de conjunto básicas em conjuntos rápidos.

A seguir estão os métodos para realizar operações definidas -

  • Intersection
  • Union
  • subtracting
let evens: Set = [10,12,14,16,18]
let odds: Set = [5,7,9,11,13]
let primes = [2,3,5,7]
odds.union(evens).sorted()
// [5,7,9,10,11,12,13,14,16,18]
odds.intersection(evens).sorted()
//[]
odds.subtracting(primes).sorted()
//[9, 11, 13]

Swift 4 dictionariessão usados ​​para armazenar listas não ordenadas de valores do mesmo tipo. O Swift 4 faz uma verificação estrita que não permite que você insira um tipo errado no dicionário, mesmo por engano.

Os dicionários Swift 4 usam identificador único conhecido como keypara armazenar um valor que posteriormente pode ser referenciado e pesquisado através da mesma chave. Ao contrário dos itens em uma matriz, os itens em umdictionarynão tem um pedido especificado. Você pode usar umdictionary quando você precisa procurar valores com base em seus identificadores.

Uma chave de dicionário pode ser um número inteiro ou uma string sem restrição, mas deve ser única em um dicionário.

Se você atribuir um dicionário criado a uma variável, ele será sempre mutável, o que significa que você pode alterá-lo adicionando, removendo ou alterando seus itens. Mas se você atribuir um dicionário a uma constante, esse dicionário é imutável e seu tamanho e conteúdo não podem ser alterados.

Criando Dicionário

Você pode criar um dicionário vazio de um certo tipo usando a seguinte sintaxe de inicializador -

var someDict = [KeyType: ValueType]()

Você pode usar a seguinte sintaxe simples para criar um dicionário vazio cuja chave será do tipo Int e os valores associados serão strings -

var someDict = [Int: String]()

Aqui está um exemplo para criar um dicionário a partir de um conjunto de valores dados -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

Inicialização baseada em sequência

O Swift 4 permite que você crie Dicionário a partir de matrizes (pares de valores-chave).

var cities = [“Delhi”,”Bangalore”,”Hyderabad”]

Você pode usar a seguinte sintaxe simples para criar um dicionário vazio cuja chave será do tipo Int e os valores associados serão strings -

var Distance = [2000,10, 620]

Aqui está um exemplo para criar um dicionário a partir de um conjunto de valores dados -

let cityDistanceDict = Dictionary(uniqueKeysWithValues: zip(cities, Distance))

As linhas de código acima criarão um dicionário com Cidades como chave e Distância como Valor -

Filtrando

O Swift 4 permite filtrar valores de um dicionário.

var closeCities = cityDistanceDict.filter { $0.value < 1000 }

Se executarmos o código acima, nosso dicionário closeCities será.

["Bangalore" : 10 , "Hyderabad" : 620]

Agrupamento de Dicionário

O Swift 4 permite criar agrupamentos de valores de Dicionário.

var cities = ["Delhi","Bangalore","Hyderabad","Dehradun","Bihar"]

Você pode usar a seguinte sintaxe simples para agrupar os valores do dicionário de acordo com o primeiro alfabeto.

var GroupedCities = Dictionary(grouping: cities ) { $0.first! }

O resultado do código acima será

["D" :["Delhi","Dehradun"], "B" : ["Bengaluru","Bihar"], "H" : ["Hyderabad"]]

Acessando Dicionários

Você pode recuperar um valor de um dicionário usando a sintaxe do subscrito, passando a chave do valor que deseja recuperar entre colchetes imediatamente após o nome do dicionário, da seguinte maneira -

var someVar = someDict[key]

Vamos verificar o exemplo a seguir para criar, inicializar e acessar valores de um dicionário -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var someVar = someDict[1]

print( "Value of key = 1 is \(someVar)" )
print( "Value of key = 2 is \(someDict[2])" )
print( "Value of key = 3 is \(someDict[3])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of key = 1 is Optional("One")
Value of key = 2 is Optional("Two")
Value of key = 3 is Optional("Three")

Modificando Dicionários

Você pode usar updateValue(forKey:)método para adicionar um valor existente a uma determinada chave do dicionário. Este método retorna um valor opcional do tipo de valor do dicionário. Aqui está um exemplo simples -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var oldVal = someDict.updateValue("New value of one", forKey: 1)
var someVar = someDict[1]

print( "Old value of key = 1 is \(oldVal)" )
print( "Value of key = 1 is \(someVar)" )
print( "Value of key = 2 is \(someDict[2])" )
print( "Value of key = 3 is \(someDict[3])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Old value of key = 1 is Optional("One")
Value of key = 1 is Optional("New value of one")
Value of key = 2 is Optional("Two")
Value of key = 3 is Optional("Three")

Você pode modificar um elemento existente de um dicionário atribuindo um novo valor em uma determinada chave, conforme mostrado no exemplo a seguir -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var oldVal = someDict[1]
someDict[1] = "New value of one"
var someVar = someDict[1]

print( "Old value of key = 1 is \(oldVal)" )
print( "Value of key = 1 is \(someVar)" )
print( "Value of key = 2 is \(someDict[2])" )
print( "Value of key = 3 is \(someDict[3])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Old value of key = 1 is Optional("One")
Value of key = 1 is Optional("New value of one")
Value of key = 2 is Optional("Two")
Value of key = 3 is Optional("Three")

Remover pares de valores-chave

Você pode usar removeValueForKey()método para remover um par de valores-chave de um dicionário. Este método remove o par de valor-chave se ele existir e retorna o valor removido ou retorna nulo se nenhum valor existia. Aqui está um exemplo simples -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var removedValue = someDict.removeValue(forKey: 2)

print( "Value of key = 1 is \(someDict[1])" )
print( "Value of key = 2 is \(someDict[2])" )
print( "Value of key = 3 is \(someDict[3])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of key = 1 is Optional("One")
Value of key = 2 is nil
Value of key = 3 is Optional("Three")

Você também pode usar a sintaxe do subscrito para remover um par de valores-chave de um dicionário atribuindo um valor de nilpara essa chave. Aqui está um exemplo simples -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

someDict[2] = nil

print( "Value of key = 1 is \(someDict[1])" )
print( "Value of key = 2 is \(someDict[2])" )
print( "Value of key = 3 is \(someDict[3])" )

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Value of key = 1 is Optional("One")
Value of key = 2 is nil
Value of key = 3 is Optional("Three")

Iterando em um Dicionário

Você pode usar um for-in loop para iterar em todo o conjunto de pares de valores-chave em um Dicionário, conforme mostrado no exemplo a seguir -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

for (index, keyValue) in someDict.enumerated() {
   print("Dictionary key \(index) - Dictionary value \(keyValue)")
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Dictionary key 2 - Dictionary value Two
Dictionary key 3 - Dictionary value Three
Dictionary key 1 - Dictionary value One

Você pode usar enumerate() função que retorna o índice do item junto com seu par (chave, valor), conforme mostrado abaixo no exemplo -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
for (key, value) in someDict.enumerated() {
   print("Dictionary key \(key) - Dictionary value \(value)")
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Dictionary key 0 - Dictionary value (key: 2, value: "Two")
Dictionary key 1 - Dictionary value (key: 3, value: "Three")
Dictionary key 2 - Dictionary value (key: 1, value: "One")

Converter para matrizes

Você pode extrair uma lista de pares de valores-chave de um determinado dicionário para construir matrizes separadas para chaves e valores. Aqui está um exemplo -

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

let dictKeys = [Int](someDict.keys)
let dictValues = [String](someDict.values)

print("Print Dictionary Keys")

for (key) in dictKeys {
   print("\(key)")
}
print("Print Dictionary Values")

for (value) in dictValues {
   print("\(value)")
}

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Print Dictionary Keys
2
3
1
Print Dictionary Values
Two
Three
One

A propriedade de contagem

Você pode usar o modo somente leitura count propriedade de um dicionário para descobrir o número de itens em um dicionário, conforme mostrado abaixo -

var someDict1:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var someDict2:[Int:String] = [4:"Four", 5:"Five"]

print("Total items in someDict1 = \(someDict1.count)")
print("Total items in someDict2 = \(someDict2.count)")

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

Total items in someDict1 = 3
Total items in someDict2 = 2

A propriedade vazia

Você pode usar somente leitura empty propriedade de um dicionário para descobrir se um dicionário está vazio ou não, conforme mostrado abaixo -

var someDict1:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var someDict2:[Int:String] = [4:"Four", 5:"Five"]
var someDict3:[Int:String] = [Int:String]()

print("someDict1 = \(someDict1.isEmpty)")
print("someDict2 = \(someDict2.isEmpty)")
print("someDict3 = \(someDict3.isEmpty)")

Quando o código acima é compilado e executado, ele produz o seguinte resultado -

someDict1 = false
someDict2 = false
someDict3 = true

Uma função é um conjunto de instruções organizadas em conjunto para realizar uma tarefa específica. Uma função do Swift 4 pode ser tão simples quanto uma função C simples ou tão complexa quanto uma função da linguagem C Objective. Ele nos permite passar valores de parâmetros locais e globais dentro das chamadas de função.

  • Function Declaration - informa ao compilador sobre o nome de uma função, tipo de retorno e parâmetros.

  • Function Definition - Fornece o corpo real da função.

As funções do Swift 4 contêm o tipo de parâmetro e seus tipos de retorno.

Definição de Função

No Swift 4, uma função é definida pela palavra-chave "func". Quando uma função é definida de novo, ela pode receber um ou vários valores como 'parâmetros' de entrada para a função e irá processar as funções no corpo principal e devolver os valores para as funções como 'tipos de retorno' de saída.

Cada função tem um nome de função, que descreve a tarefa que a função executa. Para usar uma função, você "chama" essa função com seu nome e passa valores de entrada (conhecidos como argumentos) que correspondem aos tipos de parâmetros da função. Os parâmetros da função também são chamados de 'tuplas'.

Os argumentos de uma função sempre devem ser fornecidos na mesma ordem que a lista de parâmetros da função e os valores de retorno são seguidos por →.

Sintaxe

func funcname(Parameters) -> returntype {
   Statement1
   Statement2
   ---
   Statement N
   return parameters
}

Dê uma olhada no código a seguir. O nome do aluno é declarado como tipo de dados string declarado dentro da função 'aluno' e quando a função é chamada, ela retornará o nome do aluno.

func student(name: String) -> String {
   return name
}

print(student(name: "First Program"))
print(student(name: "About Functions"))

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

First Program
About Functions

Chamando uma função

Vamos supor que definimos uma função chamada 'display' para considerar, por exemplo, para exibir os números, uma função com o nome da função 'display' é inicializada primeiro com o argumento 'no1' que contém o tipo de dados inteiro. Então, o argumento 'no1' é atribuído ao argumento 'a' que, a partir de agora, apontará para o mesmo tipo de dados inteiro. Agora, o argumento 'a' é retornado à função. Aqui, a função display () manterá o valor inteiro e retornará os valores inteiros sempre que a função for chamada.

func display(no1: Int) -> Int {
   let a = no1
   return a
}

print(display(no1: 100))
print(display(no1: 200))

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

100
200

Parâmetros e valores de retorno

O Swift 4 fornece parâmetros de função flexíveis e seus valores de retorno de valores simples a complexos. Semelhante ao de C e Objective C, funções em Swift 4 também podem assumir várias formas.

Funções com parâmetros

Uma função é acessada passando seus valores de parâmetro para o corpo da função. Podemos passar valores de parâmetros únicos para múltiplos como tuplas dentro da função.

func mult(no1: Int, no2: Int) -> Int {
   return no1*no2
}

print(mult(no1: 2, no2: 20))
print(mult(no1: 3, no2: 15))
print(mult(no1: 4, no2: 30))

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

40
45
120

Funções sem Parâmetros

Também podemos ter funções sem parâmetros.

Sintaxe

func funcname() -> datatype {
   return datatype
}

A seguir está um exemplo com uma função sem um parâmetro -

func votersname() -> String {
   return "Alice"
}
print(votersname())

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Alice

Funções com valores de retorno

As funções também são usadas para retornar valores de tipo de dados string, inteiro e float como tipos de retorno. Para descobrir o maior e o menor número em uma determinada função de matriz, 'ls' é declarado com tipos de dados inteiros pequenos e grandes.

Uma matriz é inicializada para conter valores inteiros. Em seguida, a matriz é processada e cada valor na matriz é lido e comparado com seu valor anterior. Quando o valor é menor que o anterior, ele é armazenado no argumento 'pequeno', caso contrário, é armazenado no argumento 'grande' e os valores são retornados ao chamar a função.

func ls(array: [Int]) -> (large: Int, small: Int) {
   var lar = array[0]
   var sma = array[0]

   for i in array[1..<array.count] {
      if i < sma {
         sma = i
      } else if i > lar {
         lar = i
      }
   }
   return (lar, sma)
}

let num = ls(array: [40,12,-5,78,98])
print("Largest number is: \(num.large) and smallest number is: \(num.small)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Largest number is: 98 and smallest number is: -5

Funções sem valores de retorno

Algumas funções podem ter argumentos declarados dentro da função sem nenhum valor de retorno. O seguinte programa declaraa e bcomo argumentos para a função sum (). dentro da própria função, os valores dos argumentosa e b são passados ​​invocando a chamada de função sum () e seus valores são impressos, eliminando assim os valores de retorno.

func sum(a: Int, b: Int) {
   let a = a + b
   let b = a - b
   print(a, b)
}

sum(a: 20, b: 10)
sum(a: 40, b: 10)
sum(a: 24, b: 6)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

30 20
50 40
30 24

Funções com tipos de retorno opcionais

O Swift 4 apresenta o recurso 'opcional' para se livrar de problemas, introduzindo uma medida de segurança. Considere, por exemplo, que estamos declarando o tipo de retorno dos valores da função como inteiro, mas o que acontecerá quando a função retornar um valor de string ou um valor nulo. Nesse caso, o compilador retornará um valor de erro. 'opcional' são introduzidos para se livrar desses problemas.

As funções opcionais terão duas formas 'valor' e 'nulo'. Mencionaremos 'Opcionais' com o caractere reservado chave '?' para verificar se a tupla está retornando um valor ou um valor nulo.

func minMax(array: [Int]) -> (min: Int, max: Int)? {
   if array.isEmpty { return nil }
   var currentMin = array[0]
   var currentMax = array[0]
   
   for value in array[1..<array.count] {
      if value < currentMin {
         currentMin = value
      } else if value > currentMax {
         currentMax = value
      }
   }
   return (currentMin, currentMax)
}

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
   print("min is \(bounds.min) and max is \(bounds.max)")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

min is -6 and max is 109

'' Opcionais 'são usados ​​para verificar valores' nulos 'ou lixo, consumindo muito tempo na depuração e tornando o código eficiente e legível para o usuário.

Funções locais e nomes de parâmetros externos

Nomes de parâmetros locais

Os nomes dos parâmetros locais são acessados ​​somente dentro da função.

func sample(number: Int) {
   print(number)
}

Aqui o funco número do argumento de amostra é declarado como variável interna, pois é acessado internamente pela função sample (). Aqui, o 'número' é declarado como variável local, mas a referência à variável é feita fora da função com a seguinte instrução -

func sample(number: Int) {
   print(number)
}

sample(number: 1)
sample(number: 2)
sample(number: 3)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

1
2
3

Nomes de parâmetros externos

Os nomes de parâmetros externos nos permitem nomear os parâmetros de uma função para tornar sua finalidade mais clara. Por exemplo, abaixo, você pode nomear dois parâmetros de função e, em seguida, chamar essa função da seguinte maneira -

func pow(firstArg a: Int, secondArg b: Int) -> Int {
   var res = a
   for _ in 1..<b {
      res = res * a
   }
   print(res)
   return res
}

pow(firstArg:5, secondArg:3)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

125

Parâmetros Variadic

Quando queremos definir a função com vários argumentos, podemos declarar os membros como parâmetros 'variáveis'. Os parâmetros podem ser especificados como variáveis ​​por (···) após o nome do parâmetro.

func vari<N>(members: N...){
   for i in members {
      print(i)
   }
}

vari(members: 4,3,5)
vari(members: 4.5, 3.1, 5.6)
vari(members: "Swift 4", "Enumerations", "Closures")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

4
3
5
4.5
3.1
5.6
Swift 4
Enumerations
Closures

Parâmetros Constantes, Variáveis ​​e I / O

As funções, por padrão, consideram os parâmetros como 'constantes', enquanto o usuário também pode declarar os argumentos para as funções como variáveis. Já discutimos que a palavra-chave 'let' é usada para declarar parâmetros constantes e os parâmetros variáveis ​​são definidos com a palavra-chave 'var'.

Os parâmetros de E / S no Swift 4 fornecem funcionalidade para reter os valores dos parâmetros, mesmo que seus valores sejam modificados após a chamada da função. No início da definição do parâmetro da função, a palavra-chave 'inout' é declarada para reter os valores do membro.

Ele deriva a palavra-chave 'inout', pois seus valores são passados ​​'in' para a função e seus valores são acessados ​​e modificados pelo corpo da função e é retornado 'out' da função para modificar o argumento original.

As variáveis ​​são passadas apenas como um argumento para o parâmetro in-out, pois seus valores sozinhos são modificados dentro e fora da função. Portanto, não há necessidade de declarar strings e literais como parâmetros de entrada e saída. '&' antes de um nome de variável indica que estamos passando o argumento para o parâmetro in-out.

func temp(a1: inout Int, b1: inout Int) {
   let t = a1
   a1 = b1
   b1 = t
}

var no = 2
var co = 10
temp(a1: &no, b1: &co)
print("Swapped values are \(no), \(co)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Swapped values are 10, 2

Tipos de função e seu uso

Cada função segue a função específica, considerando os parâmetros de entrada e produz o resultado desejado.

func inputs(no1: Int, no2: Int) -> Int {
   return no1/no2
}

A seguir está um exemplo -

func inputs(no1: Int, no2: Int) -> Int {
   return no1/no2
}

print(inputs(no1: 20, no2: 10))
print(inputs(no1: 36, no2: 6))

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

2
6

Aqui a função é inicializada com dois argumentos no1 e no2 como tipos de dados inteiros e seu tipo de retorno também é declarado como 'int'

Func inputstr(name: String) -> String {
   return name
}

Aqui, a função é declarada como string tipo de dados.

As funções também podem ter void tipos de dados e tais funções não retornarão nada.

func inputstr() {
   print("Swift 4 Functions")
   print("Types and its Usage")
}
inputstr()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Swift 4 Functions
Types and its Usage

A função acima é declarada como uma função nula, sem argumentos e sem valores de retorno.

Usando Tipos de Função

As funções são primeiro passadas com argumentos do tipo inteiro, float ou string e, em seguida, são passadas como constantes ou variáveis ​​para a função, conforme mencionado abaixo.

var addition: (Int, Int) -> Int = sum

Aqui sum é um nome de função com variáveis ​​inteiras 'a' e 'b' que agora é declarado como uma variável para a adição do nome da função. Doravante, as funções de adição e soma têm o mesmo número de argumentos declarados como tipo de dados inteiro e também retornam valores inteiros como referências.

func sum(a: Int, b: Int) -> Int {
   return a + b
}
var addition: (Int, Int) -> Int = sum
print("Result: \(addition(40, 89))")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Result: 129

Tipos de função como tipos de parâmetro e tipos de retorno

Também podemos passar a própria função como tipos de parâmetro para outra função.

func sum(a: Int, b: Int) -> Int {
   return a + b
}
var addition: (Int, Int) -> Int = sum
print("Result: \(addition(40, 89))")

func another(addition: (Int, Int) -> Int, a: Int, b: Int) {
   print("Result: \(addition(a, b))")
}
another(sum, 10, 20)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Result: 129
Result: 30

Funções Aninhadas

Uma função aninhada fornece a facilidade de chamar a função externa invocando a função interna.

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 0
   func decrementer() -> Int {
      overallDecrement -= total
      return overallDecrement
   }
   return decrementer
}

let decrem = calcDecrement(forDecrement: 30)
print(decrem())

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

-30

Fechamentos em Swift 4 são semelhantes aos de funções independentes organizadas como blocos e chamadas em qualquer lugar como as linguagens C e Objective C. Constantes e referências de variáveis ​​definidas dentro das funções são capturadas e armazenadas em fechamentos. As funções são consideradas como casos especiais de encerramentos e assumem as seguintes três formas -

Funções Globais Funções Aninhadas Expressões de fechamento
Tenha um nome. Não capture nenhum valor Tenha um nome. Capture valores da função envolvente Fechamentos sem nome capturam valores dos blocos adjacentes

Expressões de fechamento na linguagem Swift 4 seguem estilos de sintaxe nítidos, otimizados e leves que incluem.

  • Inferindo tipo de parâmetro e valor de retorno do contexto.
  • Retornos implícitos de encerramentos de expressão única.
  • Nomes de argumentos abreviados e
  • Sintaxe de encerramento final

Sintaxe

A seguir está uma sintaxe genérica para definir o fechamento que aceita parâmetros e retorna um tipo de dados -

{
   (parameters) −> return type in
   statements
}

A seguir está um exemplo simples -

let studname = { print("Welcome to Swift Closures") }
studname()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Welcome to Swift Closures

O encerramento a seguir aceita dois parâmetros e retorna um valor Bool -

{     
   (Int, Int) −> Bool in
   Statement1
   Statement 2
   ---
   Statement n
}

A seguir está um exemplo simples -

let divide = {
   (val1: Int, val2: Int) -> Int in 
   return val1 / val2 
}

let result = divide(200, 20)
print (result)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

10

Expressões em fechamentos

As funções aninhadas fornecem uma maneira conveniente de nomear e definir blocos de código. Em vez de representar toda a declaração de função e construções de nome são usadas para denotar funções mais curtas. A representação da função em uma declaração breve e clara com sintaxe focada é obtida por meio de expressões de encerramento.

Programa de Ordem Ascendente

A classificação de uma string é realizada pela função reservada da tecla Swift 4s "classificada", que já está disponível na biblioteca padrão. A função classificará as strings fornecidas em ordem crescente e retornará os elementos em uma nova matriz com o mesmo tamanho e tipo de dados mencionados na matriz antiga. A antiga matriz permanece a mesma.

Dois argumentos são representados dentro da função classificada -

  • Valores de tipo conhecido representados como matrizes.

  • Conteúdo da matriz (Int, Int) e retorna um valor booleano (Bool) se a matriz for classificada corretamente, retornará o valor verdadeiro, caso contrário, retornará falso.

Uma função normal com string de entrada é escrita e passada para a função classificada para obter as strings classificadas para um novo array que é mostrado abaixo -

func ascend(s1: String, s2: String) -> Bool {
   return s1 > s2
}

let stringcmp = ascend(s1: "Swift 4", s2: "great")
print (stringcmp)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

true

A matriz inicial a ser classificada para sorvete é fornecida como "Swift 4" e "ótimo". A função para classificar o array é declarada como tipo de dados string e seu tipo de retorno é mencionado como Booleano. Ambas as strings são comparadas e classificadas em ordem crescente e armazenadas em uma nova matriz. Se a classificação for realizada com sucesso, a função retornará um valor verdadeiro, caso contrário, retornará falso.

A sintaxe da expressão de fechamento usa -

  • parâmetros constantes,
  • parâmetros variáveis, e
  • parâmetros inout.

A expressão de fechamento não oferece suporte a valores padrão. Parâmetros variáveis ​​e tuplas também podem ser usados ​​como tipos de parâmetro e tipos de retorno.

let sum = {
   (no1: Int, no2: Int) -> Int in 
   return no1 + no2 
}

let digits = sum(10, 20)
print(digits)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

30

Os parâmetros e declarações de tipo de retorno mencionados na instrução de função também podem ser representados pela função de expressão de fechamento embutida com a palavra-chave 'in'. Depois de declarar o parâmetro e os tipos de retorno, a palavra-chave 'in' é usada para denotar que o corpo do encerramento.

Retornos implícitos de expressão única

Aqui, o tipo de função do segundo argumento da função classificada deixa claro que um valor Bool deve ser retornado pelo encerramento. Como o corpo da closure contém uma única expressão (s1> s2) que retorna um valor Bool, não há ambigüidade e a palavra-chave return pode ser omitida.

Para retornar uma instrução de expressão única em fechamentos de expressão, a palavra-chave 'return' é omitida em sua parte de declaração.

var count:[Int] = [5, 10, -6, 75, 20]
let descending = count.sorted(by: { n1, n2 in n1 > n2 })
let ascending = count.sorted(by: { n1, n2 in n1 < n2 })

print(descending)
print(ascending)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[75, 20, 10, 5, -6]
[-6, 5, 10, 20, 75]

A própria instrução define claramente que quando a string1 é maior que a string 2 retorna verdadeiro, caso contrário, a instrução return é omitida aqui.

Fechamentos de tipo conhecido

Considere a adição de dois números. Sabemos que a adição retornará o tipo de dados inteiro. Portanto, os fechamentos de tipo conhecido são declarados como -

let sub = {
   (no1: Int, no2: Int) -> Int in 
   return no1 - no2 
}

let digits = sub(10, 20)
print(digits)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

-10

Declarando nomes de argumentos abreviados como fechamentos

O Swift 4 fornece automaticamente nomes de argumento abreviados para fechamentos embutidos, que podem ser usados ​​para se referir aos valores dos argumentos de fechamento pelos nomes $ 0, $1, $2 e assim por diante.

var shorthand: (String, String) -> String
shorthand = { $1 }
print(shorthand("100", "200"))

Aqui, $ 0 e $ 1 referem-se ao primeiro e ao segundo argumento String da closure.

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

200

O Swift 4 facilita ao usuário representar fechamentos em linha como nomes de argumento abreviados, representando $ 0, $1, $2 --- $ n.

A lista de argumentos de closures é omitida na seção de definição quando representamos nomes de argumentos abreviados dentro de expressões de closure. Com base no tipo de função, os nomes dos argumentos abreviados serão derivados. Como o argumento abreviado é definido no corpo da expressão, a palavra-chave 'in' é omitida.

Fechamentos como funções do operador

O Swift 4 fornece uma maneira fácil de acessar os membros, fornecendo apenas funções de operador como fechos. Nos exemplos anteriores, a palavra-chave 'Bool' é usada para retornar 'verdadeiro' quando as strings são iguais, caso contrário, retorna 'falso'.

A expressão é ainda mais simples pela função do operador no fechamento como -

let numb = [98, -20, -30, 42, 18, 35]
var sortedNumbers = numb.sorted ({
   (left: Int, right: Int) -> Bool in
   return left < right
})

let asc = numb.sorted(<)
print(asc)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[-30, -20, 18, 35, 42, 98]

Fechamentos como reboques

Passar o argumento final da função para uma expressão de fechamento é declarado com a ajuda de 'Trailing Closures'. É escrito fora da função () com {}. Seu uso é necessário quando não é possível escrever a função embutida em uma única linha.

reversed = sorted(names) { $0 > $1}

onde {$ 0> $ 1} são representados como fechamentos finais declarados fora de (nomes).

import Foundation
var letters = ["North", "East", "West", "South"]

let twoletters = letters.map({ 
   (state: String) -> String in
   return state.substringToIndex(advance(state.startIndex, 2)).uppercaseString
})

let stletters = letters.map() { 
   $0.substringToIndex(advance($0.startIndex, 2)).uppercaseString 
}
print(stletters)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[NO, EA, WE, SO]

Captura de valores e tipos de referência

No Swift 4, a captura de valores de constantes e variáveis ​​é feita com a ajuda de fechamentos. Além disso, faz referência e modifica os valores para essas constantes e variáveis ​​dentro do corpo de encerramento, mesmo que as variáveis ​​não existam mais.

A captura de valores constantes e variáveis ​​é obtida usando a função aninhada, escrevendo a função com no corpo de outra função.

Uma função aninhada captura -

  • Argumentos da função externa.
  • Capture constantes e variáveis ​​definidas na função externa.

No Swift 4, quando uma constante ou variável é declarada dentro de uma função, a referência a essas variáveis ​​também é criada automaticamente pelo encerramento. Ele também fornece a facilidade de se referir a mais de duas variáveis ​​como o mesmo fechamento da seguinte forma -

let decrem = calcDecrement(forDecrement: 18)
decrem()

Aqui oneDecrement e as variáveis ​​de Decremento apontarão para o mesmo bloco de memória como referência de fechamento.

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 100
   func decrementer() -> Int {
      overallDecrement -= total
      print(overallDecrement)
      return overallDecrement
   }
   return decrementer
}

let decrem = calcDecrement(forDecrement: 18)
decrem()
decrem()
decrem()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

82
64
46

Cada vez que a função externa calcDecrement é chamada, ela invoca a função decrementer (), diminui o valor em 18 e retorna o resultado com a ajuda da função externa calcDecrement. Aqui, calcDecrement atua como um fechamento.

Mesmo que a função decrementer () não tenha nenhum argumento, o fechamento por padrão se refere às variáveis ​​'globalDecrement' e 'total' capturando seus valores existentes. A cópia dos valores para as variáveis ​​especificadas é armazenada com a nova função decrementer (). O Swift 4 lida com funções de gerenciamento de memória alocando e desalocando espaços de memória quando as variáveis ​​não estão em uso.

Uma enumeração é um tipo de dados definido pelo usuário que consiste em um conjunto de valores relacionados. Palavra-chaveenum é usado para definir o tipo de dados enumerado.

Funcionalidade de enumeração

A enumeração no Swift 4 também se assemelha à estrutura de C e do Objetivo C.

  • Ele é declarado em uma classe e seus valores são acessados ​​por meio da instância dessa classe.

  • O valor inicial do membro é definido usando inicializadores de enum.

  • Sua funcionalidade também é estendida, garantindo a funcionalidade do protocolo padrão.

Sintaxe

Enumerações são introduzidas com a palavra-chave enum e colocam toda a sua definição dentro de um par de chaves -

enum enumname {
   // enumeration values are described here
}

Por exemplo, você pode definir uma enumeração para os dias da semana da seguinte maneira -

enum DaysofaWeek {
   case Sunday
   case Monday
   ---
   case Saturday
}

Exemplo

enum names {
   case Swift
   case Closures
}

var lang = names.Closures
lang = .Closures

switch lang {
   case .Swift:
      print("Welcome to Swift")
   case .Closures:
      print("Welcome to Closures")
   default:
      print("Introduction")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Welcome to Closures

A enumeração do Swift 4 não atribui valores padrão aos membros, como C e Objetivo C. Em vez disso, os membros são explicitamente definidos por seus nomes de enumeração. O nome da enumeração deve começar com uma letra maiúscula (Ex: enum DaysofaWeek).

var weekDay = DaysofaWeek.Sunday

Aqui, o nome de enumeração 'DaysofaWeek' é atribuído a um dia da semana variável. Ele informa ao compilador que o tipo de dados pertence a Sunday e será atribuído a membros de enum subsequentes dessa classe específica. Depois que o tipo de dados do membro enum é definido, os membros podem ser acessados ​​passando valores e cálculos adicionais.

Enumeração com declaração de switch

A instrução Swift 4 'Switch' também segue a seleção de múltiplas vias. Apenas uma variável é acessada em um determinado momento com base na condição especificada. O caso padrão na instrução switch é usado para interceptar casos não especificados.

enum Climate {
   case India
   case America
   case Africa
   case Australia
}

var season = Climate.America
season = .America
switch season {
   case .India:
      print("Climate is Hot")
   case .America:
      print("Climate is Cold")
   case .Africa:
      print("Climate is Moderate")
   case .Australia:
      print("Climate is Rainy")
   
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Climate is Cold

O programa primeiro define Clima como o nome da enumeração. Em seguida, seus membros como 'Índia', 'América', 'África' e 'Austrália' são declarados pertencentes à classe 'Clima'. Agora, o membro América é atribuído a uma variável de temporada. Além disso, o caso Switch verá os valores correspondentes a .America e se ramificará para essa instrução específica. A saída será exibida como "Clima é frio". Da mesma forma, todos os membros podem ser acessados ​​por meio de instruções switch. Quando a condição não é satisfeita, ele imprime por padrão 'O clima não é previsível'.

A enumeração pode ser posteriormente classificada em valores associados e valores brutos.

Diferença entre valores associados e valores brutos

Valores Associados Valores Brutos
Tipos de dados diferentes Mesmos tipos de dados
Ex: enum {10,0.8, "Hello"} Ex: enum {10,35,50}
Os valores são criados com base em constante ou variável Valores pré-preenchidos
Varia quando declarado a cada vez O valor para o membro é o mesmo

Enum com valores associados

enum Student {
   case Name(String)
   case Mark(Int,Int,Int)
}

var studDetails = Student.Name("Swift 4")
var studMarks = Student.Mark(98,97,95)

switch studMarks {
   case .Name(let studName):
      print("Student name is: \(studName).")
   case .Mark(let Mark1, let Mark2, let Mark3):
      print("Student Marks are: \(Mark1),\(Mark2),\(Mark3).")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Student Marks are: 98,97,95.

Considere, por exemplo, acessar o nome do aluno e as marcas garantidas em três disciplinas. O nome da enumeração é declarado como aluno e os membros presentes na classe enum são o nome que pertence ao tipo de dados da string, as marcas são representadas como mark1, mark2 e mark3 do tipo de dados Inteiro. Para acessar o nome do aluno ou as notas que ele pontuou

var studDetails = Student.Name("Swift")
var studMarks = Student.Mark(98,97,95)

Agora, o caso de troca imprimirá o nome do aluno se esse bloco de caso for executado, caso contrário, ele imprimirá as marcas garantidas pelo aluno. Se ambas as condições falharem, o bloco padrão será executado.

Enum com valores brutos

Os valores brutos podem ser strings, caracteres ou qualquer um dos tipos de número inteiro ou de ponto flutuante. Cada valor bruto deve ser exclusivo em sua declaração de enumeração. Quando inteiros são usados ​​para valores brutos, eles são incrementados automaticamente se nenhum valor for especificado para alguns dos membros da enumeração.

enum Month: Int {
   case January = 1, February, March, April, May, June, July, August,
      September, October, November, December
}

let yearMonth = Month.May.rawValue
print("Value of the Month is: \(yearMonth).")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Value of the Month is: 5.

O Swift 4 fornece um bloco de construção flexível para fazer uso de construções como estruturas. Ao fazer uso dessas estruturas, uma vez podemos definir métodos e propriedades de construções.

Ao contrário de C e Objective C

  • A estrutura não precisa de arquivos e interface de implementação.

  • A estrutura nos permite criar um único arquivo e estender sua interface automaticamente a outros blocos.

Na Estrutura, os valores das variáveis ​​são copiados e passados ​​em códigos subsequentes, retornando uma cópia dos valores antigos para que os valores não possam ser alterados.

Sintaxe

Structures are defined with a 'Struct' Keyword.
struct nameStruct {
   Definition 1
   Definition 2
   ---
   Definition N
}

Definição de uma Estrutura

Considere, por exemplo, suponha que tenhamos que acessar o registro dos alunos contendo notas de três disciplinas e descobrir o total de três disciplinas. Aqui, markStruct é usado para inicializar uma estrutura com três marcas como tipo de dados 'Int'.

struct MarkStruct {
   var mark1: Int
   var mark2: Int
   var mark3: Int
}

Acessando a Estrutura e suas Propriedades

Os membros da estrutura são acessados ​​por seu nome de estrutura. As instâncias da estrutura são inicializadas pela palavra-chave 'let'.

struct studentMarks {
   var mark1 = 100
   var mark2 = 200
   var mark3 = 300
}

let marks = studentMarks()
print("Mark1 is \(marks.mark1)")
print("Mark2 is \(marks.mark2)")
print("Mark3 is \(marks.mark3)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Mark1 is 100
Mark2 is 200
Mark3 is 300

As marcas dos alunos são acessadas pelo nome da estrutura 'studentMarks'. Os membros da estrutura são inicializados como mark1, mark2, mark3 com valores de tipo inteiro. Em seguida, a estrutura studentMarks () é passada para as 'marcas' com a palavra-chave 'let'. Doravante, 'marcas' conterá os valores dos membros da estrutura. Agora os valores são impressos acessando os valores dos membros da estrutura por '.' com seus nomes inicializados.

struct MarksStruct {
   var mark: Int

   init(mark: Int) {
      self.mark = mark
   }
}

var aStruct = MarksStruct(mark: 98)
var bStruct = aStruct     // aStruct and bStruct are two structs with the same value!
bStruct.mark = 97

print(aStruct.mark)      // 98
print(bStruct.mark)      // 97

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

98
97

Melhores práticas de uso de estruturas

A linguagem Swift 4 fornece a funcionalidade para definir estruturas como tipos de dados personalizados para construir os blocos de função. As instâncias de estrutura são passadas por seu valor para os blocos definidos para futuras manipulações.

Necessidade de ter estruturas

  • Para encapsular valores de dados simples.

  • Para copiar os dados encapsulados e suas propriedades associadas por 'valores' em vez de 'referências'.

  • Estrutura para 'Copiar' e 'Referência'.

As estruturas no Swift 4 passam seus membros com seus valores e não por suas referências.

struct markStruct {
   var mark1: Int
   var mark2: Int
   var mark3: Int

   init(mark1: Int, mark2: Int, mark3: Int) {
      self.mark1 = mark1
      self.mark2 = mark2
      self.mark3 = mark3
   }
}

var marks = markStruct(mark1: 98, mark2: 96, mark3:100)
print(marks.mark1)
print(marks.mark2)
print(marks.mark3)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

98
96
100

Outro exemplo

struct markStruct {
   var mark1: Int
   var mark2: Int
   var mark3: Int
   
   init(mark1: Int, mark2: Int, mark3: Int) {
      self.mark1 = mark1
      self.mark2 = mark2
      self.mark3 = mark3
   }
}

var fail = markStruct(mark1: 34, mark2: 42, mark3: 13)

print(fail.mark1)
print(fail.mark2)
print(fail.mark3)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

34
42
13

A estrutura 'markStruct' é definida primeiro com seus membros mark1, mark2 e mark3. Agora, as variáveis ​​das classes de membro são inicializadas para conter valores inteiros. Em seguida, a cópia dos membros da estrutura é criada com a palavra-chave 'self'. Uma vez criada a cópia dos membros da estrutura, o bloco da estrutura com suas marcas de parâmetro é passado para a variável 'marcas' que agora conterá as marcas dos alunos. Em seguida, as marcas são impressas como 98, 96, 100. Próxima etapa para os mesmos membros da estrutura, outra instância chamada 'falha' é usada para apontar os mesmos membros da estrutura com marcas diferentes. Em seguida, os resultados agora são impressos como 34, 42, 13. Isso explica claramente que as estruturas terão uma cópia das variáveis ​​de membro e passarão os membros para seus próximos blocos de função.

As classes em Swift 4 são blocos de construção de construções flexíveis. Semelhante a constantes, variáveis ​​e funções, o usuário pode definir propriedades e métodos de classe. O Swift 4 nos fornece a funcionalidade de que, ao declarar classes, os usuários não precisam criar interfaces ou arquivos de implementação. O Swift 4 nos permite criar classes como um único arquivo e as interfaces externas serão criadas por padrão assim que as classes forem inicializadas.

Benefícios de ter aulas

  • A herança adquire as propriedades de uma classe para outra classe

  • A conversão de tipo permite que o usuário verifique o tipo de classe em tempo de execução

  • Os desinicializadores cuidam da liberação de recursos de memória

  • A contagem de referência permite que a instância da classe tenha mais de uma referência

Características comuns de classes e estruturas

  • Propriedades são definidas para armazenar valores
  • Os subscritos são definidos para fornecer acesso aos valores
  • Métodos são inicializados para melhorar a funcionalidade
  • O estado inicial é definido por inicializadores
  • A funcionalidade é expandida além dos valores padrão
  • Confirmando os padrões de funcionalidade do protocolo

Sintaxe

Class classname {
   Definition 1
   Definition 2
   --- 
   Definition N
}

Definição de Classe

class student {
   var studname: String
   var mark: Int 
   var mark2: Int 
}

A sintaxe para criar instâncias

let studrecord = student()

Exemplo

class MarksStruct {
   var mark: Int
   init(mark: Int) {
      self.mark = mark
   }
}

class studentMarks {
   var mark = 300
}

let marks = studentMarks()
print("Mark is \(marks.mark)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Mark is 300

Acessando Propriedades de Classe como Tipos de Referência

As propriedades da classe podem ser acessadas por '.' sintaxe. O nome da propriedade é separado por um '.' após o nome da instância.

class MarksStruct {
   var mark: Int
   init(mark: Int) {
      self.mark = mark
   }
}

class studentMarks {
   var mark1 = 300
   var mark2 = 400
   var mark3 = 900
}

let marks = studentMarks()
print("Mark1 is \(marks.mark1)")
print("Mark2 is \(marks.mark2)")
print("Mark3 is \(marks.mark3)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Mark1 is 300
Mark2 is 400
Mark3 is 900

Operadores de identidade de classe

Classes em Swift 4 referem-se a várias constantes e variáveis ​​apontando para uma única instância. Para saber sobre as constantes e variáveis ​​que apontam para uma determinada instância de classe, são usados ​​operadores de identidade. As instâncias de classe são sempre passadas por referência. Nas classes NSString, NSArray e NSDictionary, as instâncias são sempre atribuídas e transmitidas como uma referência a uma instância existente, em vez de uma cópia.

Idêntico aos operadores Não idêntico aos operadores
O operador usado é (===) O operador usado é (! ==)
Retorna verdadeiro quando duas constantes ou variáveis ​​apontando para a mesma instância Retorna verdadeiro quando duas constantes ou variáveis ​​apontam para uma instância diferente
class SampleClass: Equatable {
   let myProperty: String
   init(s: String) {
      myProperty = s
   }
}

func ==(lhs: SampleClass, rhs: SampleClass) -> Bool {
   return lhs.myProperty == rhs.myProperty
}

let spClass1 = SampleClass(s: "Hello")
let spClass2 = SampleClass(s: "Hello")

spClass1 === spClass2 // false
print("\(spClass1)")

spClass1 !== spClass2 // true
print("\(spClass2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

main.SampleClass
main.SampleClass

A linguagem Swift 4 fornece propriedades para classe, enumeração ou estrutura para associar valores. As propriedades podem ser classificadas em propriedades armazenadas e propriedades computadas.

Diferença entre propriedades armazenadas e propriedades computadas

Propriedade Armazenada Propriedade Computada
Armazene valores constantes e variáveis ​​como instância Calcule um valor em vez de armazená-lo
Fornecido por classes e estruturas Fornecido por classes, enumerações e estruturas

As propriedades Armazenadas e Computadas estão associadas ao tipo de instância. Quando as propriedades são associadas aos seus valores de tipo, ela é definida como 'Propriedades de tipo'. Propriedades armazenadas e calculadas são geralmente associadas a instâncias de um tipo específico. No entanto, as propriedades também podem ser associadas ao próprio tipo. Essas propriedades são conhecidas como propriedades de tipo. Observadores de propriedade também são usados

  • Para observar o valor das propriedades armazenadas
  • Para observar a propriedade da subclasse herdada derivada da superclasse

Propriedades Armazenadas

O Swift 4 introduz o conceito de propriedade armazenada para armazenar as instâncias de constantes e variáveis. As propriedades armazenadas das constantes são definidas pela palavra-chave 'let' e as propriedades armazenadas das variáveis ​​são definidas pela palavra-chave 'var'.

  • Durante a definição, a propriedade armazenada fornece 'valor padrão'
  • Durante a inicialização, o usuário pode inicializar e modificar os valores iniciais
struct Number {
   var digits: Int
   let pi = 3.1415
}

var n = Number(digits: 12345)
n.digits = 67

print("\(n.digits)")
print("\(n.pi)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

67
3.1415

Considere a seguinte linha no código acima -

let pi = 3.1415

Aqui, a variável pi é inicializada como um valor de propriedade armazenado com a instância pi = 3,1415. Portanto, sempre que a instância for referenciada, ela conterá apenas o valor 3,1415.

Outro método para ter propriedades armazenadas é ter como estruturas constantes. Portanto, toda a instância das estruturas será considerada como 'Propriedades armazenadas das constantes'.

struct Number {
   var digits: Int
   let numbers = 3.1415
}

var n = Number(digits: 12345)
n.digits = 67

print("\(n.digits)")
print("\(n.numbers)")
n.numbers = 8.7

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

error: cannot assign to 'numbers' in 'n'
n.numbers = 8.7

Em vez de reinicializar o 'número' para 8,7, ele retornará uma mensagem de erro indicando que o 'número' é declarado como constante.

Propriedade Armazenada Preguiçosa

Swift 4 fornece uma propriedade flexível chamada 'Lazy Stored Property' onde não calculará os valores iniciais quando a variável for inicializada pela primeira vez. O modificador 'preguiçoso' é usado antes da declaração da variável para tê-la como uma propriedade armazenada preguiçosa.

Propriedades preguiçosas são usadas -

  • Para atrasar a criação do objeto.
  • Quando a propriedade depende de outras partes de uma classe, que ainda não são conhecidas
class sample {
   lazy var no = number()    // `var` declaration is required.
}

class number {
   var name = "Swift 4"
}

var firstsample = sample()
print(firstsample.no.name)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Swift 4

Variáveis ​​de instância

Em Objective C, as propriedades armazenadas também têm variáveis ​​de instância para fins de backup para armazenar os valores declarados na propriedade armazenada.

O Swift 4 integra esses dois conceitos em uma única declaração de 'propriedade armazenada'. Em vez de ter uma variável de instância correspondente e valor de backup, a 'propriedade armazenada' contém todas as informações integradas definidas em um único local sobre a propriedade das variáveis ​​por nome de variável, tipo de dados e funcionalidades de gerenciamento de memória.

Propriedades Computadas

Em vez de armazenar os valores calculados, as propriedades fornecem um getter e um setter opcional para recuperar e definir outras propriedades e valores indiretamente.

class sample {
   var no1 = 0.0, no2 = 0.0
   var length = 300.0, breadth = 150.0

   var middle: (Double, Double) {
      get {
         return (length / 2, breadth / 2)
      }
      
      set(axis){
         no1 = axis.0 - (length / 2)
         no2 = axis.1 - (breadth / 2)
      }
   }
}

var result = sample()
print(result.middle)
result.middle = (0.0, 10.0)

print(result.no1)
print(result.no2)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

(150.0, 75.0)
-150.0
-65.0

Quando uma propriedade computada deixa o novo valor como indefinido, o valor padrão será definido para aquela variável particular.

Propriedades calculadas como propriedades somente leitura

Uma propriedade somente leitura na propriedade computada é definida como uma propriedade com getter, mas sem setter. É sempre usado para retornar um valor. As variáveis ​​são posteriormente acessadas por meio de um '.' Sintaxe, mas não pode ser definida para outro valor.

class film {
   var head = ""
   var duration = 0.0
   var metaInfo: [String:String] {
      return [
         "head": self.head,
         "duration":"\(self.duration)"
      ]
   }
}

var movie = film()
movie.head = "Swift 4 Properties"
movie.duration = 3.09

print(movie.metaInfo["head"]!)
print(movie.metaInfo["duration"]!)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Swift 4 Properties
3.09

Propriedades calculadas como observadores de propriedade

No Swift 4, para observar e responder aos valores das propriedades, são usados ​​Observadores de Propriedades. Sempre que os valores das propriedades são definidos, os observadores das propriedades são chamados. Exceto as propriedades armazenadas lazy, podemos adicionar observadores de propriedade à propriedade 'herdada' pelo método de 'substituição'.

Observadores de propriedade podem ser definidos por qualquer

  • Antes de armazenar o valor - willset

  • Depois de armazenar o novo valor - didset

  • Quando uma propriedade é definida em um inicializador, os observadores willset e didset não podem ser chamados.

class Samplepgm {
   var counter: Int = 0 {
      willSet(newTotal){
         print("Total Counter is: \(newTotal)")
      }
      
      didSet {
         if counter > oldValue {
            print("Newly Added Counter \(counter - oldValue)")
         }
      }
   }
}

let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Total Counter is: 100
Newly Added Counter 100
Total Counter is: 800
Newly Added Counter 700

Variáveis ​​locais e globais

Variáveis ​​locais e globais são declaradas para calcular e observar as propriedades.

Variáveis ​​Locais Variáveis ​​globais
Variáveis ​​que são definidas em uma função, método ou contexto de encerramento. Variáveis ​​que são definidas fora do contexto de função, método, encerramento ou tipo.
Usado para armazenar e recuperar valores. Usado para armazenar e recuperar valores.
As propriedades armazenadas são usadas para obter e definir os valores. As propriedades armazenadas são usadas para obter e definir os valores.
Propriedades computadas também são usadas. Propriedades computadas também são usadas.

Propriedades de tipo

As propriedades são definidas na seção de definição de tipo com chaves {} e o escopo das variáveis ​​também é definido anteriormente. Para definir as propriedades de tipo para os tipos de valor, a palavra-chave 'estática' é usada e para os tipos de classe a palavra-chave 'class' é usada.

Sintaxe

struct Structname {
   static var storedTypeProperty = " "
   static var computedTypeProperty: Int {
      // return an Int value here
   }
}

enum Enumname {
   static var storedTypeProperty = " "
   static var computedTypeProperty: Int {
      // return an Int value here
   }
}

class Classname {
   class var computedTypeProperty: Int {
      // return an Int value here
   }
}

Consultando e definindo propriedades

Assim como as propriedades de instância, as propriedades de tipo são consultadas e definidas com '.' Sintaxe apenas no tipo em vez de apontar para a instância.

struct StudMarks {
   static let markCount = 97
   static var totalCount = 0
   
   var InternalMarks: Int = 0 {
      didSet {
         if InternalMarks > StudMarks.markCount {
            InternalMarks = StudMarks.markCount
         }
         if InternalMarks > StudMarks.totalCount {
            StudMarks.totalCount = InternalMarks
         }
      }
   }
}

var stud1Mark1 = StudMarks()
var stud1Mark2 = StudMarks()

stud1Mark1.InternalMarks = 98
print(stud1Mark1.InternalMarks)

stud1Mark2.InternalMarks = 87
print(stud1Mark2.InternalMarks)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

97
87

Na linguagem Swift 4, as funções associadas a tipos específicos são chamadas de métodos. Em Objective C Classes são usadas para definir métodos, enquanto a linguagem Swift 4 fornece ao usuário flexibilidade para ter métodos para Classes, Estruturas e Enumerações.

Métodos de Instância

Na linguagem Swift 4, as instâncias de Classes, Structures e Enumeration são acessadas através dos métodos de instância.

Métodos de instância fornecem funcionalidade

  • Para acessar e modificar propriedades da instância
  • funcionalidade relacionada à necessidade da instância

O método de instância pode ser escrito dentro das {} chaves. Ele tem acesso implícito a métodos e propriedades da instância do tipo. Quando uma instância específica do tipo é chamada, ela obtém acesso a essa instância específica.

Sintaxe

func funcname(Parameters) -> returntype {
   Statement1
   Statement2
   ---
   Statement N
   return parameters
}

Exemplo

class calculations {
   let a: Int
   let b: Int
   let res: Int

   init(a: Int, b: Int) {
      self.a = a
      self.b = b
      res = a + b
   }
   
   func tot(c: Int) -> Int {
      return res - c
   }
   
   func result() {
      print("Result is: \(tot(c: 20))")
      print("Result is: \(tot(c: 50))")
   }
}
let pri = calculations(a: 600, b: 300)
pri.result()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Result is: 880
Result is: 850

Class Calculations define dois métodos de instância -

  • init () é definido para adicionar dois números aeb e armazená-los no resultado 'res'
  • tot () é usado para subtrair o 'res' da passagem do valor 'c'

Finalmente, para imprimir os métodos de cálculos com valores para a e b é chamado. Os métodos de instância são acessados ​​com '.' sintaxe de ponto

Nomes de parâmetros locais e externos

As funções do Swift 4 descrevem declarações locais e globais para suas variáveis. Da mesma forma, as convenções de nomenclatura dos Métodos do Swift 4 também se parecem com as do Objetivo C. Mas as características das declarações de nomes de parâmetros locais e globais são diferentes para funções e métodos. O primeiro parâmetro no Swift 4 são referidos por nomes de preposição como 'com', 'para' e 'por' para facilitar o acesso às convenções de nomenclatura.

O Swift 4 fornece flexibilidade nos métodos, declarando o primeiro nome do parâmetro como nomes de parâmetros locais e os nomes de parâmetros restantes como nomes de parâmetros globais. Aqui, 'no1' é declarado pelos métodos Swift 4 como nomes de parâmetros locais. 'no2' é usado para declarações globais e acessado por meio do programa.

class division {
   var count: Int = 0
   func incrementBy(no1: Int, no2: Int) {
      count = no1 / no2
      print(count)
   }
}

let counter = division()
counter.incrementBy(no1: 1800, no2: 3)
counter.incrementBy(no1: 1600, no2: 5)
counter.incrementBy(no1: 11000, no2: 3)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

600
320
3666

Nome do parâmetro externo com # e símbolo _

Mesmo que os métodos do Swift 4 forneçam os primeiros nomes de parâmetros para declarações locais, o usuário tem a possibilidade de modificar os nomes de parâmetros de declarações locais para globais. Isso pode ser feito prefixando o símbolo '#' com o primeiro nome do parâmetro. Ao fazer isso, o primeiro parâmetro pode ser acessado globalmente em todos os módulos.

Quando o usuário precisa acessar os nomes de parâmetro subsequentes com um nome externo, o nome do método é substituído com a ajuda do símbolo '_'.

class multiplication {
   var count: Int = 0
   func incrementBy(no1: Int, no2: Int) {
      count = no1 * no2
      print(count)
   }
}

let counter = multiplication()

counter.incrementBy(no1: 800, no2: 3)
counter.incrementBy(no1: 100, no2: 5)
counter.incrementBy(no1: 15000, no2: 3)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

2400
500
45000

Propriedade própria em métodos

Os métodos têm uma propriedade implícita conhecida como 'self' para todas as suas instâncias de tipo definidas. A propriedade 'Self' é usada para referir as instâncias atuais para seus métodos definidos.

class calculations {
   let a: Int
   let b: Int
   let res: Int

   init(a: Int, b: Int) {
      self.a = a
      self.b = b
      res = a + b
      print("Inside Self Block: \(res)")
   }
   
   func tot(c: Int) -> Int {
      return res - c
   }
   
   func result() {
      print("Result is: \(tot(c: 20))")
      print("Result is: \(tot(c: 50))")
   }
}

let pri = calculations(a: 600, b: 300)
let sum = calculations(a: 1200, b: 300)

pri.result()
sum.result()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Inside Self Block: 900
Inside Self Block: 1500
Result is: 880
Result is: 850
Result is: 1480
Result is: 1450

Modificando Tipos de Valor de Métodos de Instância

Na linguagem Swift 4, as estruturas e enumerações pertencem a tipos de valor que não podem ser alterados por seus métodos de instância. No entanto, a linguagem Swift 4 fornece flexibilidade para modificar os tipos de valor por 'mutação' do comportamento. Mutate fará qualquer alteração nos métodos de instância e retornará à forma original após a execução do método. Além disso, pela propriedade 'self' uma nova instância é criada para sua função implícita e irá substituir o método existente após sua execução

struct area {
   var length = 1
   var breadth = 1
   
   func area() -> Int {
      return length * breadth
   }
   mutating func scaleBy(res: Int) {
      length *= res
      breadth *= res
      print(length)
      print(breadth)
   }
}

var val = area(length: 3, breadth: 5)
val.scaleBy(res: 3)
val.scaleBy(res: 30)
val.scaleBy(res: 300)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

9
15
270
450
81000
135000

Propriedade própria para método mutante

Métodos mutantes combinados com a propriedade 'self' atribuem uma nova instância ao método definido.

struct area {
   var length = 1
   var breadth = 1
   func area() -> Int {
      return length * breadth
   }
   mutating func scaleBy(res: Int) {
      self.length *= res
      self.breadth *= res
      print(length)
      print(breadth)
   }
}

var val = area(length: 3, breadth: 5)
val.scaleBy(res: 13)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado. -

39
65

Métodos de Tipo

Quando uma determinada instância de um método é chamada, ela é chamada como um método Instance; e quando o método chama um tipo específico de método, ele é chamado de 'Métodos de tipo'. Os métodos de tipo para 'classes' são definidos pela palavra-chave 'func' e os métodos de tipo de estruturas e enumerações são definidos com a palavra-chave 'estática' antes da palavra-chave 'func'.

Os métodos de tipo são chamados e acessados ​​por '.' sintaxe onde, em vez de chamar uma instância particular, todo o método é invocado.

class Math {
   class func abs(number: Int) -> Int {
      if number < 0 {
         return (-number)
      } else {
         return number
      }
   }
}

struct absno {
   static func abs(number: Int) -> Int {
      if number < 0 {
         return (-number)
      } else {
         return number
      }
   }
}

let no = Math.abs(number: -35)
let num = absno.abs(number: -5)

print(no)
print(num)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado. -

35
5

O acesso aos elementos membros de uma coleção, sequência e lista em Classes, Estruturas e Enumerações é feito com o auxílio de subscritos. Esses subscritos são usados ​​para armazenar e recuperar os valores com a ajuda do índice. Os elementos do array são acessados ​​com a ajuda de someArray [index] e seus elementos de membro subsequentes em uma instância de Dictionary podem ser acessados ​​como someDicitonary [key].

Para um único tipo, os subscritos podem variar de declarações simples a múltiplas. Podemos usar o subscrito apropriado para sobrecarregar o tipo de valor de índice passado ao subscrito. Os subscritos também variam de dimensão única a dimensão múltipla de acordo com os requisitos dos usuários para suas declarações de tipo de dados de entrada.

Sintaxe de declaração de subscrito e seu uso

Vamos recapitular as propriedades calculadas. Os subscritos também seguem a mesma sintaxe das propriedades computadas. Para consultar instâncias de tipo, os subscritos são escritos dentro de um colchete seguido do nome da instância. A sintaxe do subscrito segue a mesma estrutura de sintaxe do 'método de instância' e da sintaxe de 'propriedade computada'. A palavra-chave 'subscript' é usada para definir subscritos e o usuário pode especificar parâmetros únicos ou múltiplos com seus tipos de retorno. Os subscritos podem ter propriedades de leitura-gravação ou somente leitura e as instâncias são armazenadas e recuperadas com a ajuda das propriedades 'getter' e 'setter' como propriedades computadas.

Sintaxe

subscript(index: Int) −> Int {
   get {
      // used for subscript value declarations
   }
   set(newValue) {
      // definitions are written here
   }
}

Exemplo 1

struct subexample {
   let decrementer: Int
   subscript(index: Int) -> Int {
      return decrementer / index
   }
}
let division = subexample(decrementer: 100)

print("The number is divisible by \(division[9]) times")
print("The number is divisible by \(division[2]) times")
print("The number is divisible by \(division[3]) times")
print("The number is divisible by \(division[5]) times")
print("The number is divisible by \(division[7]) times")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

The number is divisible by 11 times
The number is divisible by 50 times
The number is divisible by 33 times
The number is divisible by 20 times
The number is divisible by 14 times

Exemplo 2

class daysofaweek {
   private var days = ["Sunday", "Monday", "Tuesday", "Wednesday",
      "Thursday", "Friday", "saturday"]
   subscript(index: Int) -> String {
      get {
         return days[index]
      }
      set(newValue) {
         self.days[index] = newValue
      }
   }
}
var p = daysofaweek()

print(p[0])
print(p[1])
print(p[2])
print(p[3])

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Sunday
Monday
Tuesday
Wednesday

Opções no subscrito

Os subscritos levam parâmetros de entrada únicos a múltiplos e esses parâmetros de entrada também pertencem a qualquer tipo de dados. Eles também podem usar parâmetros variáveis ​​e variadic. Os subscritos não podem fornecer valores de parâmetro padrão ou usar parâmetros de entrada e saída.

Definir vários subscritos é denominado como 'sobrecarga de subscritos', onde uma classe ou estrutura pode fornecer várias definições de subscritos conforme necessário. Esses vários subscritos são inferidos com base nos tipos de valores que são declarados entre as chaves de subscrito.

struct Matrix {
   let rows: Int, columns: Int
   var print: [Double]
   init(rows: Int, columns: Int) {
      self.rows = rows
      self.columns = columns
      print = Array(count: rows * columns, repeatedValue: 0.0)
   }
   subscript(row: Int, column: Int) -> Double {
      get {
         return print[(row * columns) + column]
      }
      set {
         print[(row * columns) + column] = newValue
      }
   }
}
var mat = Matrix(rows: 3, columns: 3)

mat[0,0] = 1.0
mat[0,1] = 2.0
mat[1,0] = 3.0
mat[1,1] = 5.0

print("\(mat[0,0])")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

1.0

O subscrito do Swift 4 oferece suporte a declarações de parâmetro único a vários parâmetros para tipos de dados apropriados. O programa declara a estrutura 'Matrix' como uma matriz de matriz 2 * 2 dimensional para armazenar tipos de dados 'Double'. O parâmetro Matrix é inserido com tipos de dados Inteiro para declarar linhas e colunas.

Uma nova instância para a Matrix é criada passando a contagem de linhas e colunas para a inicialização, conforme mostrado abaixo.

var mat = Matrix(rows: 3, columns: 3)

Os valores da matriz podem ser definidos passando os valores da linha e da coluna no subscrito, separados por uma vírgula, conforme mostrado abaixo.

mat[0,0] = 1.0  
mat[0,1] = 2.0
mat[1,0] = 3.0
mat[1,1] = 5.0

A capacidade de assumir mais formas é definida como Herança. Geralmente, uma classe pode herdar métodos, propriedades e funcionalidades de outra classe. As classes podem ser categorizadas em subclasse e superclasse.

  • Sub Class - quando uma classe herda propriedades, métodos e funções de outra classe, ela é chamada de subclasse

  • Super Class - A classe que contém propriedades, métodos e funções para herdar outras classes de si mesma é chamada de superclasse

As classes do Swift 4 contêm superclasses que chama e acessa métodos, propriedades, funções e métodos de substituição. Além disso, os observadores de propriedade também são usados ​​para adicionar uma propriedade e modificar os métodos de propriedade armazenados ou computados.

Classe Base

Uma Classe que não herda métodos, propriedades ou funções de outra classe é chamada de 'Classe Base'.

class StudDetails {
   var stname: String!
   var mark1: Int!
   var mark2: Int!
   var mark3: Int!
   
   init(stname: String, mark1: Int, mark2: Int, mark3: Int) {
      self.stname = stname
      self.mark1 = mark1
      self.mark2 = mark2
      self.mark3 = mark3
   }
}

let stname = "Swift 4"
let mark1 = 98
let mark2 = 89
let mark3 = 76

print(stname)
print(mark1)
print(mark2)
print(mark3)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Swift 4
98
89
76

A classe com o nome da classe StudDetails são definidas como uma classe base aqui, que é usada para conter o nome dos alunos e três disciplinas marcadas como mark1, mark2 e mark3. A palavra-chave 'let' é usada para inicializar o valor da classe base e o valor da classe base é exibido no playground com a ajuda da função 'print'.

Subclasse

O ato de basear uma nova classe em uma classe existente é definido como 'Subclasse'. A subclasse herda as propriedades, métodos e funções de sua classe base. Para definir uma subclasse ':' é usado antes do nome da classe base

class StudDetails {
   var mark1: Int;
   var mark2: Int;
   
   init(stm1:Int, results stm2:Int) {
      mark1 = stm1;
      mark2 = stm2;
   }
   func print() {
      print("Mark1:\(mark1), Mark2:\(mark2)")
   }
}

class display : StudDetails {
   init() {
      super.init(stm1: 93, results: 89)
   }
}

let marksobtained = display()
marksobtained.print()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Mark1:93, Mark2:89

A classe 'StudDetails' é definida como superclasse onde as notas dos alunos são declaradas e a subclasse 'display' é usada para herdar as notas de sua superclasse. A subclasse define as marcas dos alunos e chama o método print () para exibir as marcas dos alunos.

Substituindo

Acessar a instância da superclasse, métodos de tipo, instância, propriedades de tipo e subclasse de subscritos fornece o conceito de substituição. A palavra-chave 'override' é usada para substituir os métodos declarados na superclasse.

Acesso a métodos, propriedades e subscrições da Superclasse

A palavra-chave 'super' é usada como um prefixo para acessar os métodos, propriedades e subscritos declarados na superclasse

Substituindo Acesso a métodos, propriedades e subscritos
Métodos super.somemethod ()
Propriedades super.someProperty ()
Subscritos super [someIndex]

Substituição de métodos

Os métodos de instância e tipo herdados podem ser substituídos pela palavra-chave 'override' para nossos métodos definidos em nossa subclasse. Aqui print () é sobrescrito na subclasse para acessar a propriedade type mencionada na superclasse print (). Além disso, uma nova instância da superclasse cricket () é criada como 'cricinstance'.

class cricket {
   func print() {
      print("Welcome to Swift 4 Super Class")
   }
}

class tennis: cricket {
   override func print() {
      print("Welcome to Swift 4 Sub Class")
   }
}

let cricinstance = cricket()
cricinstance.print()

let tennisinstance = tennis()
tennisinstance.print()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Welcome to Swift Super Class
Welcome to Swift Sub Class

Substituição de propriedade

Você pode substituir uma instância herdada ou propriedade de classe para fornecer seu próprio getter e setter personalizado para essa propriedade ou adicionar observadores de propriedade para permitir que a propriedade de substituição observe quando o valor da propriedade subjacente muda.

Substituindo getters e setters de propriedade

O Swift 4 permite que o usuário forneça getter e setter personalizados para substituir a propriedade herdada, seja ela armazenada ou computada. A subclasse não conhece o nome e o tipo da propriedade herdada. Portanto, é essencial que o usuário precise especificar na subclasse, o nome e o tipo da propriedade de substituição especificada na superclasse.

Isso pode ser feito de duas maneiras -

  • Quando setter é definido para substituir a propriedade, o usuário deve definir getter também.

  • Quando não queremos modificar o getter da propriedade herdada, podemos simplesmente passar o valor herdado pela sintaxe 'super.someProperty' para a superclasse.

class Circle {
   var radius = 12.5
   var area: String {
      return "of rectangle for \(radius) "
   }
}

class Rectangle: Circle {
   var print = 7
   override var area: String {
      return super.area + " is now overridden as \(print)"
   }
}

let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("Radius \(rect.area)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Radius of rectangle for 25.0  is now overridden as 3

Substituindo observadores de propriedade

Quando uma nova propriedade precisa ser adicionada para uma propriedade herdada, o conceito de 'substituição de propriedade' é introduzido no Swift 4. Isso notifica o usuário quando o valor da propriedade herdada é alterado. Mas a substituição não é aplicável para propriedades armazenadas constantes herdadas e propriedades computadas somente leitura herdadas.

class Circle {
   var radius = 12.5
   var area: String {
     return "of rectangle for \(radius) "
   }
}

class Rectangle: Circle {
   var print = 7
   override var area: String {
      return super.area + " is now overridden as \(print)"
   }
}

let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("Radius \(rect.area)")

class Square: Rectangle {
   override var radius: Double {
      didSet {
         print = Int(radius/5.0)+1
      }
   }
}

let sq = Square()
sq.radius = 100.0
print("Radius \(sq.area)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Radius of rectangle for 25.0  is now overridden as 3
Radius of rectangle for 100.0  is now overridden as 21

Propriedade final para evitar a substituição

Quando o usuário não deseja que outros acessem métodos de superclasse, propriedades ou subscritos, o Swift 4 introduz a propriedade 'final' para evitar a substituição. Uma vez que a propriedade 'final' é declarada, os subscritos não permitirão que os métodos da superclasse, propriedades e seus subscritos sejam sobrescritos. Não há provisão para ter propriedade 'final' em 'superclasse'. Quando a propriedade 'final' é declarada, o usuário fica restrito a criar outras subclasses.

final class Circle {
   final var radius = 12.5
   var area: String {
      return "of rectangle for \(radius) "
   }
}

class Rectangle: Circle {
   var print = 7
   override var area: String {
      return super.area + " is now overridden as \(print)"
   }
}

let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("Radius \(rect.area)")

class Square: Rectangle {
   override var radius: Double {
      didSet {
         print = Int(radius/5.0)+1
      }
   }
}

let sq = Square()
sq.radius = 100.0
print("Radius \(sq.area)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

<stdin>:14:18: error: var overrides a 'final' var
override var area: String {
^
<stdin>:7:9: note: overridden declaration is here
var area: String {
^
<stdin>:12:11: error: inheritance from a final class 'Circle'
class Rectangle: Circle {
^
<stdin>:25:14: error: var overrides a 'final' var
override var radius: Double {
^
<stdin>:6:14: note: overridden declaration is here
final var radius = 12.5

Visto que a superclasse é declarada como 'final' e seus tipos de dados também são declarados como 'final', o programa não permitirá a criação de subclasses posteriormente e gerará erros.

Classes, estruturas e enumerações, uma vez declaradas no Swift 4, são inicializadas para preparar a instância de uma classe. O valor inicial é inicializado para a propriedade armazenada e também para novas instâncias, os valores são inicializados para prosseguir. A palavra-chave para criar a função de inicialização é realizada pelo método 'init ()'. O inicializador do Swift 4 difere do Objective-C porque não retorna nenhum valor. Sua função é verificar a inicialização de instâncias recém-criadas antes de seu processamento. O Swift 4 também fornece processo de 'desinicialização' para realizar operações de gerenciamento de memória, uma vez que as instâncias são desalocadas.

Função de inicializador para propriedades armazenadas

A propriedade armazenada deve inicializar as instâncias para suas classes e estruturas antes de processar as instâncias. As propriedades armazenadas usam o inicializador para atribuir e inicializar valores, eliminando assim a necessidade de chamar observadores de propriedade. O inicializador é usado na propriedade armazenada

  • Para criar um valor inicial.

  • Para atribuir o valor da propriedade padrão na definição da propriedade.

  • Para inicializar uma instância para um determinado tipo de dados, 'init ()' é usado. Nenhum argumento é passado dentro da função init ().

Sintaxe

init() {
   //New Instance initialization goes here
}

Exemplo

struct rectangle {
   var length: Double
   var breadth: Double
   init() {
      length = 6
      breadth = 12
   }
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

area of rectangle is 72.0

Aqui, a estrutura 'retângulo' é inicializada com comprimento e largura dos membros como tipos de dados 'Double'. O método Init () é usado para inicializar os valores para os membros recém-criados length e double. A área do retângulo é calculada e retornada chamando a função retângulo.

Definindo valores de propriedade por padrão

A linguagem Swift 4 fornece a função Init () para inicializar os valores de propriedade armazenados. Além disso, o usuário tem a capacidade de inicializar os valores da propriedade por padrão ao declarar os membros da classe ou da estrutura. Quando a propriedade assume o mesmo valor sozinho em todo o programa, podemos declará-lo apenas na seção de declaração em vez de inicializá-lo em init (). Definir valores de propriedade por padrão habilita o usuário quando a herança é definida para classes ou estruturas.

struct rectangle {
   var length = 6
   var breadth = 12
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

area of rectangle is 72

Aqui, em vez de declarar comprimento e largura em init (), os valores são inicializados na própria declaração.

Inicialização de Parâmetros

Na linguagem Swift 4, o usuário tem a capacidade de inicializar parâmetros como parte da definição do inicializador usando init ().

struct Rectangle {
   var length: Double
   var breadth: Double
   var area: Double
   
   init(fromLength length: Double, fromBreadth breadth: Double) {
      self.length = length
      self.breadth = breadth
      area = length * breadth
   }
   init(fromLeng leng: Double, fromBread bread: Double) {
      self.length = leng
      self.breadth = bread
      area = leng * bread
   }
}

let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("area is: \(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("area is: \(are.area)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

area is: 72.0
area is: 432.0

Parâmetros locais e externos

Os parâmetros de inicialização têm nomes de parâmetros locais e globais semelhantes aos dos parâmetros de função e método. A declaração do parâmetro local é usada para acessar dentro do corpo de inicialização e a declaração do parâmetro externo é usada para chamar o inicializador. Os inicializadores do Swift 4 diferem do inicializador de função e método porque não identificam qual inicializador é usado para chamar quais funções.

Para superar isso, o Swift 4 introduz um nome externo automático para cada parâmetro em init (). Este nome externo automático é equivalente ao nome local escrito antes de cada parâmetro de inicialização.

struct Days {
   let sunday, monday, tuesday: Int
   init(sunday: Int, monday: Int, tuesday: Int) {
      self.sunday = sunday
      self.monday = monday
      self.tuesday = tuesday
   }
   init(daysofaweek: Int) {
      sunday = daysofaweek
      monday = daysofaweek
      tuesday = daysofaweek
   }
}

let week = Days(sunday: 1, monday: 2, tuesday: 3)
print("Days of a Week is: \(week.sunday)")
print("Days of a Week is: \(week.monday)")
print("Days of a Week is: \(week.tuesday)")

let weekdays = Days(daysofaweek: 4)
print("Days of a Week is: \(weekdays.sunday)")
print("Days of a Week is: \(weekdays.monday)")
print("Days of a Week is: \(weekdays.tuesday)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Days of a Week is: 1
Days of a Week is: 2
Days of a Week is: 3
Days of a Week is: 4
Days of a Week is: 4
Days of a Week is: 4

Parâmetros sem nomes externos

Quando um nome externo não é necessário para inicializar, o sublinhado '_' é usado para substituir o comportamento padrão.

struct Rectangle {
   var length: Double
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

area is: 180.0
area is: 370.0
area is: 110.0

Tipos de propriedade opcionais

Quando a propriedade armazenada em alguma instância não retorna nenhum valor, essa propriedade é declarada com um tipo 'opcional' indicando que 'nenhum valor' é retornado para esse tipo específico. Quando a propriedade armazenada é declarada como 'opcional', ela inicializa automaticamente o valor como 'nulo' durante a própria inicialização.

struct Rectangle {
   var length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

Modificando propriedades constantes durante a inicialização

A inicialização também permite que o usuário modifique o valor da propriedade constante. Durante a inicialização, a propriedade da classe permite que suas instâncias de classe sejam modificadas pela superclasse e não pela subclasse. Considere, por exemplo, no programa anterior, 'comprimento' é declarado como 'variável' na classe principal. A variável de programa abaixo 'comprimento' é modificada como variável 'constante'.

struct Rectangle {
   let length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

Inicializadores padrão

Os inicializadores padrão fornecem uma nova instância para todas as suas propriedades declaradas da classe base ou estrutura com valores padrão.

class defaultexample {
   var studname: String?
   var stmark = 98
   var pass = true
}
var result = defaultexample()

print("result is: \(result.studname)")
print("result is: \(result.stmark)")
print("result is: \(result.pass)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado. -

result is: nil
result is: 98
result is: true

O programa acima é definido com o nome da classe como 'exemplo padrão'. Três funções-membro são inicializadas por padrão como 'studname?' para armazenar valores 'nulos', 'stmark' como 98 e 'passar' como valor booleano 'verdadeiro'. Da mesma forma, os valores dos membros da classe podem ser inicializados como padrão antes de processar os tipos de membros da classe.

Inicializadores de membro para tipos de estrutura

Quando os inicializadores personalizados não são fornecidos pelo usuário, os tipos de estrutura no Swift 4 receberão automaticamente o 'inicializador de membro'. Sua função principal é inicializar as novas instâncias de estrutura com a inicialização padrão por membro e, em seguida, as propriedades da nova instância são passadas para a inicialização por nome de membro.

struct Rectangle {
   var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)

print("Area of rectangle is: \(area.length)")
print("Area of rectangle is: \(area.breadth)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Area of rectangle is: 24.0
Area of rectangle is: 32.0

As estruturas são inicializadas por padrão para suas funções de associação durante a inicialização para 'comprimento' como '100,0' e 'largura' como '200,0'. Mas os valores são substituídos durante o processamento das variáveis ​​de comprimento e largura como 24.0 e 32.0.

Delegação de inicializador para tipos de valor

Delegação de inicializador é definida como inicializadores de chamada de outros inicializadores. Sua principal função é atuar como capacidade de reutilização para evitar a duplicação de código em vários inicializadores.

struct Stmark {
   var mark1 = 0.0, mark2 = 0.0
}
struct stdb {
   var m1 = 0.0, m2 = 0.0
}

struct block {
   var average = stdb()
   var result = Stmark()
   init() {}
   init(average: stdb, result: Stmark) {
      self.average = average
      self.result = result
   }

   init(avg: stdb, result: Stmark) {
      let tot = avg.m1 - (result.mark1 / 2)
      let tot1 = avg.m2 - (result.mark2 / 2)
      self.init(average: stdb(m1: tot, m2: tot1), result: result)
   }
}

let set1 = block()
print("student result is: \(set1.average.m1, set1.average.m2)
\(set1.result.mark1, set1.result.mark2)")

let set2 = block(average: stdb(m1: 2.0, m2: 2.0),
result: Stmark(mark1: 5.0, mark2: 5.0))
print("student result is: \(set2.average.m1, set2.average.m2)
\(set2.result.mark1, set2.result.mark2)")

let set3 = block(avg: stdb(m1: 4.0, m2: 4.0),
result: Stmark(mark1: 3.0, mark2: 3.0))
print("student result is: \(set3.average.m1, set3.average.m2)
\(set3.result.mark1, set3.result.mark2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

(0.0,0.0) (0.0,0.0)
(2.0,2.0) 5.0,5.0)
(2.5,2.5) (3.0,3.0)

Regras para delegação de inicializador

Tipos de valor Tipos de classe
A herança não é compatível com tipos de valor, como estruturas e enumerações. A referência a outros inicializadores é feita por meio de self.init A herança é suportada. Verifica se todos os valores de propriedade armazenados foram inicializados

Herança e inicialização de classe

Os tipos de classe têm dois tipos de inicializadores para verificar se as propriedades armazenadas definidas recebem um valor inicial, a saber, inicializadores designados e inicializadores de conveniência.

Inicializadores designados e inicializadores de conveniência

Inicializador Designado Inicializador de conveniência
Considerado como inicializa primária para uma classe Considerado como suporte para inicialização de uma classe
Todas as propriedades da classe são inicializadas e o inicializador de superclasse apropriado é chamado para inicialização posterior O inicializador designado é chamado com o inicializador de conveniência para criar uma instância de classe para um caso de uso específico ou tipo de valor de entrada
Pelo menos um inicializador designado é definido para cada classe Não há necessidade de ter inicializadores de conveniência obrigatoriamente definidos quando a classe não requer inicializadores.
Init (parâmetros) {instruções} conveniência init (parâmetros) {instruções}

Programa para inicializadores designados

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int // new subclass storage
   init(no1 : Int, no2 : Int) {
      self.no2 = no2 // initialization
      super.init(no1:no1) // redirect to superclass
   }
}

let res = mainClass(no1: 10)
let print = subClass(no1: 10, no2: 20)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

res is: 10
res is: 10
res is: 20

Programa para inicializadores de conveniência

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int
   init(no1 : Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   // Requires only one parameter for convenient method
   override convenience init(no1: Int) {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

res is: 20
res is: 30
res is: 50

Herança e substituição do inicializador

O Swift 4 não permite que suas subclasses herdem seus inicializadores de superclasse para seus tipos de membro por padrão. A herança é aplicável aos inicializadores de superclasse apenas até certo ponto, o que será discutido em Herança do inicializador automático.

Quando o usuário precisa ter inicializadores definidos na superclasse, a subclasse com inicializadores deve ser definida pelo usuário como implementação customizada. Quando a substituição deve ser realizada pela subclasse, a palavra-chave 'substituição' da superclasse deve ser declarada.

class sides {
   var corners = 4
   var description: String {
      return "\(corners) sides"
   }
}

let rectangle = sides()
print("Rectangle: \(rectangle.description)")

class pentagon: sides {
   override init() {
      super.init()
      corners = 5
   }
}

let bicycle = pentagon()
print("Pentagon: \(bicycle.description)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Rectangle: 4 sides
Pentagon: 5 sides

Inicializadores designados e de conveniência em ação

class Planet {
   var name: String
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}

let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")

class planets: Planet {
   var count: Int
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Planet name is: Mercury
No Planets like that: [No Planets]

Inicializador disponível

O usuário deve ser notificado quando houver alguma falha no inicializador ao definir uma classe, estrutura ou valores de enumeração. A inicialização de variáveis ​​às vezes se torna uma falha devido a -

  • Valores de parâmetro inválidos.
  • Ausência de fonte externa necessária.
  • Condição que impede o sucesso da inicialização.

Para capturar exceções lançadas pelo método de inicialização, o Swift 4 produz uma inicialização flexível chamada 'inicializador failable' para notificar o usuário de que algo foi deixado despercebido durante a inicialização da estrutura, classe ou membros de enumeração. A palavra-chave para capturar o inicializador failable é 'init?'. Além disso, inicializadores failable e não failable não podem ser definidos com os mesmos tipos e nomes de parâmetro.

struct studrecord {
   let stname: String
   init?(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}
let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Student name is specified
Student name is left blank

Inicializadores disponíveis para enumerações

A linguagem Swift 4 fornece a flexibilidade de ter inicializadores Failable para enumerações também para notificar o usuário quando os membros da enumeração deixam de inicializar os valores.

enum functions {
   case a, b, c, d
   init?(funct: String) {
      switch funct {
      case "one":
         self = .a
      case "two":
         self = .b
      case "three":
         self = .c
      case "four":
         self = .d
      default:
         return nil
      }
   }
}
let result = functions(funct: "two")

if result != nil {
   print("With In Block Two")
}
let badresult = functions(funct: "five")

if badresult == nil {
   print("Block Does Not Exist")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

With In Block Two
Block Does Not Exist

Inicializadores disponíveis para classes

Um inicializador failable quando declarado com enumerações e estruturas alerta uma falha de inicialização em qualquer circunstância dentro de sua implementação. No entanto, o inicializador failable nas classes alertará a falha somente depois que as propriedades armazenadas forem definidas com um valor inicial.

class studrecord {
   let studname: String!
   init?(studname: String) {
      self.studname = studname
      if studname.isEmpty { return nil }
   }
}

if let stname = studrecord(studname: "Failable Initializers") {
   print("Module is \(stname.studname)")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Module is Optional("Failable Initializers")

Substituindo um inicializador disponível

Assim como o de inicializar, o usuário também tem a capacidade de substituir um inicializador failable da superclasse dentro da subclasse. A inicialização failable da superclasse também pode ser substituída por um inicializador não fiável da subclasse.

O inicializador de subclasse não pode delegar ao inicializador de superclasse ao substituir um inicializador de superclasse failable por uma inicialização de subclasse não failable.

Um inicializador não failable nunca pode delegar a um inicializador failable.

O programa fornecido a seguir descreve os inicializadores failable e não failable.

class Planet {
   var name: String
   
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}
let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")
   
class planets: Planet {
   var count: Int
   
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Planet name is: Mercury
No Planets like that: [No Planets]

O init! Inicializador disponível

Swift 4 fornece 'init?' para definir um inicializador failable de instância opcional. Para definir uma instância opcional implicitamente desembrulhada do tipo específico 'init!' é especificado.

struct studrecord {
let stname: String

   init!(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}

let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Student name is specified
Student name is left blank

Inicializadores necessários

Para declarar toda e qualquer subclasse da palavra-chave initialize 'required' precisa ser definida antes da função init ().

class classA {
   required init() {
      var a = 10
      print(a)
   }
}

class classB: classA {
   required init() {
      var b = 30
      print(b)
   }
}

let res = classA()
let print = classB()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

10
30
10

Antes que uma instância de classe precise ser desalocada, o 'desinicializador' deve ser chamado para desalocar o espaço de memória. A palavra-chave 'deinit' é usada para desalocar os espaços de memória ocupados pelos recursos do sistema. A desinicialização está disponível apenas em tipos de classe.

Desinicialização para desalocar espaço de memória

O Swift 4 desaloca automaticamente suas instâncias quando elas não são mais necessárias, para liberar recursos. O Swift 4 lida com o gerenciamento de memória de instâncias por meio de contagem automática de referência (ARC), conforme descrito em Contagem automática de referência. Normalmente, você não precisa realizar uma limpeza manual quando suas instâncias são desalocadas. No entanto, quando estiver trabalhando com seus próprios recursos, pode ser necessário realizar alguma limpeza adicional. Por exemplo, se você criar uma classe personalizada para abrir um arquivo e gravar alguns dados nele, pode ser necessário fechar o arquivo antes que a instância da classe seja desalocada.

var counter = 0; // for reference counting
class baseclass {
   init() {
      counter++;
   }
   deinit {
      counter--;
   }
}
var print: baseclass? = baseclass()

print(counter)
print = nil
print(counter)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

1
0

Quando a instrução print = nil é omitida, os valores do contador são mantidos, pois não é desinicializado.

var counter = 0; // for reference counting

class baseclass {
   init() {
      counter++;
   }
   deinit {
      counter--;
   }
}
var print: baseclass? = baseclass()
print(counter)
print(counter)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

1
1

As funções de gerenciamento de memória e seu uso são administrados na linguagem Swift 4 por meio de contagem automática de referência (ARC). O ARC é usado para inicializar e desinicializar os recursos do sistema, liberando assim os espaços de memória usados ​​pelas instâncias da classe quando as instâncias não são mais necessárias. O ARC mantém o controle das informações sobre os relacionamentos entre nossas instâncias de código para gerenciar os recursos de memória de maneira eficaz.

Funções do ARC

  • O ARC aloca um pedaço de memória para armazenar as informações toda vez que uma nova instância de classe é criada por init ().

  • Informações sobre o tipo de instância e seus valores são armazenados na memória.

  • Quando a instância da classe não é mais necessária, ela libera automaticamente o espaço de memória por deinit () para armazenamento e recuperação adicionais da instância da classe.

  • O ARC mantém o controle das propriedades, constantes e variáveis ​​das instâncias de classe de referência, de forma que deinit () seja aplicado apenas às instâncias não utilizadas.

  • O ARC mantém uma 'referência forte' a essas propriedades, constantes e variáveis ​​de instância de classe para restringir a desalocação quando a instância de classe está em uso.

Programa ARC

class StudDetails {
   var stname: String!
   var mark: Int!
   
   init(stname: String, mark: Int) {
      self.stname = stname
      self.mark = mark
   }
   deinit {
      print("Deinitialized \(self.stname)")
      print("Deinitialized \(self.mark)")
   }
}

let stname = "Swift 4"
let mark = 98

print(stname)
print(mark)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Swift 4
98

Instâncias da classe ARC Strong Reference Cycles

class studmarks {
   let name: String
   var stud: student?
   
   init (name: String) {
      print("Initializing: \(name)")
      self.name = name
   }
   deinit {
      print("Deallocating: \(self.name)")
   }
}

class student {
   let name: String
   var strname: studmarks?
   
   init (name: String) {
      print("Initializing: \(name)")
      self.name = name
   }
   deinit {
      print("Deallocating: \(self.name)")
   }
}

var shiba: studmarks?
var mari: student?

shiba = studmarks(name: "Swift 4")
mari = student(name: "ARC")

shiba!.stud = mari
mari!.strname = shiba

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Initializing: Swift 4
Initializing: ARC

Referências ARC fracas e sem proprietário

As propriedades de tipo de classe têm duas maneiras de resolver fortes ciclos de referência -

  • Referências fracas
  • Referências não proprietárias

Essas referências são usadas para permitir que uma instância se refira a outras instâncias em um ciclo de referência. Então, as instâncias podem se referir a cada uma das instâncias, em vez de se preocupar com o ciclo de referência forte. Quando o usuário sabe que alguma instância pode retornar valores 'nulos', podemos apontar isso usando uma referência fraca. Quando a instância vai retornar algo em vez de nulo, declare-o com uma referência sem proprietário.

Programa de Referência Fraca

class module {
   let name: String
   init(name: String) { self.name = name }
   var sub: submodule?
   deinit { print("\(name) Is The Main Module") }
}

class submodule {
   let number: Int
   init(number: Int) { self.number = number }
   weak var topic: module?

   deinit { print("Sub Module with its topic number is \(number)") }
}

var toc: module?
var list: submodule?
toc = module(name: "ARC")
list = submodule(number: 4)
toc!.sub = list
list!.topic = toc

toc = nil
list = nil

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

ARC Is The Main Module
Sub Module with its topic number is 4

Programa de referência sem proprietário

class student {
   let name: String
   var section: marks?
   init(name: String) {
      self.name = name
   }
   deinit { print("\(name)") }
}

class marks {
   let marks: Int
   unowned let stname: student
   
   init(marks: Int, stname: student) {
      self.marks = marks
      self.stname = stname
   }
   deinit { print("Marks Obtained by the student is \(marks)") }
}

var module: student?
module = student(name: "ARC")
module!.section = marks(marks: 98, stname: module!)
module = nil

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

ARC
Marks Obtained by the student is 98

Ciclos de referência fortes para fechamentos

Quando atribuímos um encerramento à propriedade da instância da classe e ao corpo do encerramento para capturar uma instância particular, o ciclo de referência forte pode ocorrer. Uma referência forte ao encerramento é definida por 'self.someProperty' ou 'self.someMethod ()'. Ciclos de referência fortes são usados ​​como tipos de referência para os fechamentos.

class HTMLElement {
   let samplename: String
   let text: String?
   
   lazy var asHTML: () -> String = {
      if let text = self.text {
         return "<\(self.samplename)>\(text)</\(self.samplename)>"
      } else {
         return "<\(self.samplename) />"
      }
   }
   init(samplename: String, text: String? = nil) {
      self.samplename = samplename
      self.text = text
   }
   deinit {
      print("\(samplename) is being deinitialized")
   }
}

var paragraph: HTMLElement? = HTMLElement(samplename: "p", text: "Welcome to Closure SRC")
print(paragraph!.asHTML())

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

<p>Welcome to Closure SRC</p>

Referências fracas e sem proprietário

Quando o fechamento e a instância se referem um ao outro, o usuário pode definir a captura em um fechamento como uma referência sem proprietário. Então, não permitiria que o usuário desalocasse a instância ao mesmo tempo. Quando a instância em algum momento retornar um valor 'nulo', defina o encerramento com a instância fraca.

class HTMLElement {
   let module: String
   let text: String?
   
   lazy var asHTML: () -> String = {
      [unowned self] in
      if let text = self.text {
         return "<\(self.module)>\(text)</\(self.module)>"
      } else {
         return "<\(self.module) />"
      }
   }
   init(module: String, text: String? = nil) {
      self.module = module
      self.text = text
   }
   deinit {
      print("\(module) the deinit()")
   }
}

var paragraph: HTMLElement? = HTMLElement(module: "Inside", text: "ARC Weak References")
print(paragraph!.asHTML())
paragraph = nil

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

<Inside>ARC Weak References</Inside>
Inside the deinit()

O processo de consultar, chamar propriedades, subscritos e métodos em um opcional que pode ser 'nulo' é definido como encadeamento opcional. O encadeamento opcional retorna dois valores -

  • se o opcional contém um 'valor', chamar sua propriedade, métodos e subscritos relacionados retorna valores

  • se o opcional contém um valor 'nulo', todas as suas propriedades relacionadas, métodos e subscritos retornam nulo

Uma vez que várias consultas a métodos, propriedades e subscritos são agrupados, a falha em uma cadeia afetará toda a cadeia e resultará em valor 'nulo'.

Encadeamento opcional como alternativa ao desembrulhamento forçado

O encadeamento opcional é especificado após o valor opcional com '?' para chamar uma propriedade, método ou subscrito quando o valor opcional retorna alguns valores.

Encadeamento opcional '?' Acesso a métodos, propriedades e subscritosCadeamento opcional '!' para forçar o desembrulhar
? é colocado após o valor opcional para chamar a propriedade, método ou subscrito ! é colocado após o valor opcional para chamar a propriedade, método ou subscrito para forçar o desempacotamento do valor
Falha normalmente quando o opcional é 'nulo' O desempacotamento forçado aciona um erro de tempo de execução quando o opcional é 'nulo'

Programa para encadeamento opcional com '!'

class ElectionPoll {
   var candidate: Pollbooth?
}

lass Pollbooth {
   var name = "MP"
}

let cand = ElectionPoll()
let candname = cand.candidate!.name

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

fatal error: unexpectedly found nil while unwrapping an Optional value
0 Swift 4 0x0000000103410b68
llvm::sys::PrintStackTrace(__sFILE*) + 40
1 Swift 4 0x0000000103411054 SignalHandler(int) + 452
2 libsystem_platform.dylib 0x00007fff9176af1a _sigtramp + 26
3 libsystem_platform.dylib 0x000000000000000b _sigtramp + 1854492939
4 libsystem_platform.dylib 0x00000001074a0214 _sigtramp + 1976783636
5 Swift 4 0x0000000102a85c39
llvm::JIT::runFunction(llvm::Function*, std::__1::vector > const&) + 329
6 Swift 4 0x0000000102d320b3
llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*,
std::__1::vector<std::__1::basic_string, std::__1::allocator >,
std::__1::allocator<std::__1::basic_string, std::__1::allocator > > > const&,
char const* const*) + 1523
7 Swift 4 0x000000010296e6ba Swift 4::RunImmediately(Swift
4::CompilerInstance&, std::__1::vector<std::__1::basic_string,
std::__1::allocator >, std::__1::allocator<std::__1::basic_string,
std::__1::allocator > > > const&, Swift 4::IRGenOptions&, Swift 4::SILOptions
const&) + 1066
8 Swift 4 0x000000010275764b frontend_main(llvm::ArrayRef,
char const*, void*) + 5275
9 Swift 4 0x0000000102754a6d main + 1677
10 libdyld.dylib 0x00007fff8bb9e5c9 start + 1
11 libdyld.dylib 0x000000000000000c start + 1950751300
Stack dump:
0. Program arguments:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/
usr/bin/Swift 4 -frontend -interpret - -target x86_64-apple-darwin14.0.0 -
target-cpu core2 -sdk
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/
SDKs/MacOSX10.10.sdk -module-name main
/bin/sh: line 47: 15672 Done cat <<'SWIFT 4'
import Foundation
</std::__1::basic_string</std::__1::basic_string</std::__1::basic_string</std::
__1::basic_string

O programa acima declara 'pesquisa eleitoral' como nome da classe e contém 'candidato' como função de membro. A subclasse é declarada como 'poll booth' e 'name' como sua função de associação que é inicializada como 'MP'. A chamada para a superclasse é inicializada criando uma instância 'cand' com '!' Opcional. Uma vez que os valores não são declarados em sua classe base, o valor 'nulo' é armazenado, retornando assim um erro fatal pelo procedimento de desdobramento forçado.

Programa para encadeamento opcional com '?'

class ElectionPoll {
   var candidate: Pollbooth?
}

class Pollbooth {
   var name = "MP"
}
let cand = ElectionPoll()

if let candname = cand.candidate?.name {
   print("Candidate name is \(candname)")
} else {
   print("Candidate name cannot be retreived")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Candidate name cannot be retreived

O programa acima declara 'pesquisa eleitoral' como nome da classe e contém 'candidato' como função de associação. A subclasse é declarada como 'poll booth' e 'name' como sua função de associação que é inicializada como 'MP'. A chamada para a superclasse é inicializada criando uma instância 'cand' com opcional '?' Como os valores não são declarados em sua classe base, o valor 'nil' é armazenado e impresso no console pelo bloco do manipulador else.

Definindo classes de modelo para propriedades de encadeamento e acesso opcionais

A linguagem Swift 4 também fornece o conceito de encadeamento opcional, para declarar mais de uma subclasse como classes de modelo. Este conceito será muito útil para definir modelos complexos e para acessar as propriedades, métodos e subpropriedades dos subscritos.

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var street: String?

   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let rectname = rectangle()
if let rectarea = rectname.print?.cprint {
   print("Area of rectangle is \(rectarea)")
} else {
   print("Rectangle Area is not specified")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Rectangle Area is not specified

Métodos de chamada por meio de encadeamento opcional

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }

   func circleprint() {
      print("Area of Circle is: \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if circname.print?.circleprint() != nil {
   print("Area of circle is specified)")
} else {
   print("Area of circle is not specified")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Area of circle is not specified

A função circleprint () declarada dentro da subclasse circle () é chamada criando uma instância chamada 'circname'. A função retornará um valor se contiver algum valor, caso contrário, retornará alguma mensagem de impressão definida pelo usuário, verificando a declaração 'if circname.print? .Circleprint ()! = Nil'.

Acessando subscritos por meio de encadeamento opcional

O encadeamento opcional é usado para definir e recuperar um valor subscrito para validar se a chamada para esse subscrito retorna um valor. '?' é colocado antes das chaves do subscrito para acessar o valor opcional no subscrito particular.

Programa 1

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname =  radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if let radiusName = circname.print?[0].radiusname {
   print("The first room name is \(radiusName).")
} else {
   print("Radius is not specified.")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Radius is not specified.

No programa acima, os valores de instância para a função de pertinência 'radiusName' não são especificados. Portanto, a chamada do programa para a função retornará apenas a outra parte, enquanto para retornar os valores temos que definir os valores para a função de pertinência particular.

Programa 2

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")
let printing = circle()

printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing

if let radiusName = circname.print?[0].radiusname {
   print("Radius is measured in \(radiusName).")
} else {
   print("Radius is not specified.")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Radius is measured in Units.

No programa acima, os valores de instância para a função de pertinência 'radiusName' são especificados. Portanto, a chamada do programa para a função agora retornará valores.

Acessando Subscritos de Tipo Opcional

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }

   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")

let printing = circle()
printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing

var area = ["Radius": [35, 45, 78, 101], "Circle": [90, 45, 56]]
area["Radius"]?[1] = 78
area["Circle"]?[1]--

print(area["Radius"]?[0])
print(area["Radius"]?[1])
print(area["Radius"]?[2])
print(area["Radius"]?[3])

print(area["Circle"]?[0])
print(area["Circle"]?[1])
print(area["Circle"]?[2])

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Optional(35)
Optional(78)
Optional(78)
Optional(101)
Optional(90)
Optional(44)
Optional(56)

Os valores opcionais para subscritos podem ser acessados ​​referindo-se a seus valores subscritos. Pode ser acessado como subscrito [0], subscrito [1] etc. Os valores subscritos padrão para 'raio' são atribuídos primeiro como [35, 45, 78, 101] e para 'Círculo' [90, 45, 56]] . Em seguida, os valores subscritos são alterados como Raio [0] a 78 e Círculo [1] a 45.

Vinculando vários níveis de encadeamento

Múltiplas subclasses também podem ser vinculadas com seus métodos de superclasse, propriedades e subscritos por encadeamento opcional.

Encadeamento múltiplo de opcional pode ser vinculado -

Se a recuperação do tipo não for opcional, o encadeamento opcional retornará um valor opcional. Por exemplo, se String por meio de encadeamento opcional, ele retornará String? Valor

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?

   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if let radiusName = circname.print?[0].radiusname {
   print("The first room name is \(radiusName).")
} else {
   print("Radius is not specified.")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Radius is not specified.

No programa acima, os valores de instância para a função de pertinência 'radiusName' não são especificados. Conseqüentemente, a chamada do programa para a função retornará apenas outra parte, enquanto para retornar os valores temos que definir os valores para a função de pertinência particular.

Se o tipo de recuperação já for opcional, o encadeamento opcional também retornará um valor opcional. Por exemplo, se String? É acessado através de encadeamento opcional ele retornará String? Valor..

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("The number of rooms is \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()
circname.print?[0] = radius(radiusname: "Diameter")
let printing = circle()

printing.area.append(radius(radiusname: "Units"))
printing.area.append(radius(radiusname: "Meter"))
circname.print = printing

if let radiusName = circname.print?[0].radiusname {
   print("Radius is measured in \(radiusName).")
} else {
   print("Radius is not specified.")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Radius is measured in Units.

No programa acima, os valores de instância para a função de pertinência 'radiusName' são especificados. Portanto, a chamada do programa para a função agora retornará valores.

Encadeamento de métodos com valores de retorno opcionais

O encadeamento opcional também é usado para acessar métodos definidos de subclasses.

class rectangle {
   var print: circle?
}

class circle {
   var area = [radius]()
   var cprint: Int {
      return area.count
   }
   subscript(i: Int) -> radius {
      get {
         return area[i]
      }
      set {
         area[i] = newValue
      }
   }
   func circleprint() {
      print("Area of Circle is: \(cprint)")
   }
   var rectarea: circumference?
}

class radius {
   let radiusname: String
   init(radiusname: String) { self.radiusname = radiusname }
}

class circumference {
   var circumName: String?
   var circumNumber: String?
   var circumarea: String?
   
   func buildingIdentifier() -> String? {
      if circumName != nil {
         return circumName
      } else if circumNumber != nil {
         return circumNumber
      } else {
         return nil
      }
   }
}

let circname = rectangle()

if circname.print?.circleprint() != nil {
   print("Area of circle is specified)")
} else {
   print("Area of circle is not specified")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Area of circle is not specified

Para validar o tipo de uma instância, 'Type Casting' entra em ação na linguagem Swift 4. É usado para verificar se o tipo de instância pertence a uma superclasse ou subclasse específica ou se está definido em sua própria hierarquia.

A conversão de tipo do Swift 4 fornece dois operadores 'é' para verificar o tipo de um valor e 'como' e para converter o valor do tipo para um tipo diferente. A conversão de tipo também verifica se o tipo de instância segue um determinado padrão de conformidade de protocolo.

Definindo uma Hierarquia de Classe

A conversão de tipo é usada para verificar o tipo de instâncias para descobrir se pertence a um determinado tipo de classe. Além disso, ele verifica a hierarquia de classes e suas subclasses para verificar e lançar essas instâncias para torná-las uma mesma hierarquia.

class Subjects {
   var physics: String
   init(physics: String) {
      self.physics = physics
   }
}

class Chemistry: Subjects {
   var equations: String
   init(physics: String, equations: String) {
      self.equations = equations
      super.init(physics: physics)
   }
}

class Maths: Subjects {
   var formulae: String
   init(physics: String, formulae: String) {
      self.formulae = formulae
      super.init(physics: physics)
   }
}

let sa = [ Chemistry(physics: "solid physics", equations: "Hertz"),
   Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz")]

let samplechem = Chemistry(physics: "solid physics", equations: "Hertz")
print("Instance physics is: \(samplechem.physics)")
print("Instance equation is: \(samplechem.equations)")

let samplemaths = Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz")
print("Instance physics is: \(samplemaths.physics)")
print("Instance formulae is: \(samplemaths.formulae)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Instance physics is: solid physics
Instance equation is: Hertz
Instance physics is: Fluid Dynamics
Instance formulae is: Giga Hertz

Verificação de tipo

A verificação de tipo é feita com o operador 'is'. O operador de verificação do tipo 'is' verifica se a instância pertence a um tipo de subclasse específico e retorna 'verdadeiro' se pertencer a essa instância, caso contrário, retornará 'falso'.

class Subjects {
   var physics: String
   init(physics: String) {
      self.physics = physics
   }
}

class Chemistry: Subjects {
   var equations: String
   init(physics: String, equations: String) {
      self.equations = equations
      super.init(physics: physics)
   }
}

class Maths: Subjects {
   var formulae: String
   init(physics: String, formulae: String) {
      self.formulae = formulae
      super.init(physics: physics)
   }
}

let sa = [
   Chemistry(physics: "solid physics", equations: "Hertz"),
   Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz"),
   Chemistry(physics: "Thermo physics", equations: "Decibels"),
   Maths(physics: "Astro Physics", formulae: "MegaHertz"),
   Maths(physics: "Differential Equations", formulae: "Cosine Series")]

let samplechem = Chemistry(physics: "solid physics", equations: "Hertz")
print("Instance physics is: \(samplechem.physics)")
print("Instance equation is: \(samplechem.equations)")

let samplemaths = Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz")
print("Instance physics is: \(samplemaths.physics)")
print("Instance formulae is: \(samplemaths.formulae)")

var chemCount = 0
var mathsCount = 0
for item in sa {
   if item is Chemistry {
      ++chemCount
   } else if item is Maths {
      ++mathsCount
   }
}

print("Subjects in chemistry contains \(chemCount) topics and maths contains \(mathsCount) topics")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Instance physics is: solid physics
Instance equation is: Hertz
Instance physics is: Fluid Dynamics
Instance formulae is: Giga Hertz
Subjects in chemistry contains 2 topics and maths contains 3 topics

Downcasting

O downcast do tipo de subclasse pode ser feito com dois operadores (as? E as!). 'As?' retorna um valor opcional quando o valor retorna nulo. É usado para verificar o downcast bem-sucedido.

'Como!' retorna o desempacotamento forçado, conforme discutido no encadeamento opcional, quando o downcasting retorna o valor nulo. É usado para acionar erro de tempo de execução em caso de falha de downcast

class Subjects {
   var physics: String
   init(physics: String) {
      self.physics = physics
   }
}

class Chemistry: Subjects {
   var equations: String
   init(physics: String, equations: String) {
      self.equations = equations
      super.init(physics: physics)
   }
}

class Maths: Subjects {
   var formulae: String
   init(physics: String, formulae: String) {
      self.formulae = formulae
      super.init(physics: physics)
   }
}

let sa = [
   Chemistry(physics: "solid physics", equations: "Hertz"),
   Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz"),
   Chemistry(physics: "Thermo physics", equations: "Decibels"),
   Maths(physics: "Astro Physics", formulae: "MegaHertz"),
   Maths(physics: "Differential Equations", formulae: "Cosine Series")]

let samplechem = Chemistry(physics: "solid physics", equations: "Hertz")
print("Instance physics is: \(samplechem.physics)")
print("Instance equation is: \(samplechem.equations)")

let samplemaths = Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz")
print("Instance physics is: \(samplemaths.physics)")
print("Instance formulae is: \(samplemaths.formulae)")

var chemCount = 0
var mathsCount = 0

for item in sa {
   if let print = item as? Chemistry {
      print("Chemistry topics are: '\(print.physics)', \(print.equations)")
   } else if let example = item as? Maths {
      print("Maths topics are: '\(example.physics)', \(example.formulae)")
   }
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Instance physics is: solid physics
Instance equation is: Hertz
Instance physics is: Fluid Dynamics
Instance formulae is: Giga Hertz
Chemistry topics are: 'solid physics', Hertz
Maths topics are: 'Fluid Dynamics', Giga Hertz
Chemistry topics are: 'Thermo physics', Decibels
Maths topics are: 'Astro Physics', MegaHertz
Maths topics are: 'Differential Equations', Cosine Series

Typecasting: qualquer e qualquer objeto

A palavra-chave 'Qualquer' é usada para representar uma instância que pertence a qualquer tipo, incluindo tipos de função.

class Subjects {
   var physics: String
   init(physics: String) {
      self.physics = physics
   }
}

class Chemistry: Subjects {
   var equations: String
   init(physics: String, equations: String) {
      self.equations = equations
      super.init(physics: physics)
   }
}

class Maths: Subjects {
   var formulae: String
   init(physics: String, formulae: String) {
      self.formulae = formulae
      super.init(physics: physics)
   }
}

let sa = [
   Chemistry(physics: "solid physics", equations: "Hertz"),
   Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz"),
   Chemistry(physics: "Thermo physics", equations: "Decibels"),
   Maths(physics: "Astro Physics", formulae: "MegaHertz"),
   Maths(physics: "Differential Equations", formulae: "Cosine Series")]

let samplechem = Chemistry(physics: "solid physics", equations: "Hertz")
print("Instance physics is: \(samplechem.physics)")
print("Instance equation is: \(samplechem.equations)")

let samplemaths = Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz")
print("Instance physics is: \(samplemaths.physics)")
print("Instance formulae is: \(samplemaths.formulae)")

var chemCount = 0
var mathsCount = 0

for item in sa {
   if let print = item as? Chemistry {
      print("Chemistry topics are: '\(print.physics)', \(print.equations)")
   } else if let example = item as? Maths {
      print("Maths topics are: '\(example.physics)', \(example.formulae)")
   }
}

var exampleany = [Any]()

exampleany.append(12)
exampleany.append(3.14159)
exampleany.append("Example for Any")
exampleany.append(Chemistry(physics: "solid physics", equations: "Hertz"))

for print in exampleany {
   switch print {
      case let someInt as Int:
         print("Integer value is \(someInt)")
      case let someDouble as Double where someDouble > 0:
         print("Pi value is \(someDouble)")
      case let someString as String:
         print("\(someString)")
      case let phy as Chemistry:   
         print("Topics '\(phy.physics)', \(phy.equations)")
      default:
         print("None")
   }
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Instance physics is: solid physics
Instance equation is: Hertz
Instance physics is: Fluid Dynamics
Instance formulae is: Giga Hertz
Chemistry topics are: 'solid physics', Hertz
Maths topics are: 'Fluid Dynamics', Giga Hertz
Chemistry topics are: 'Thermo physics', Decibels
Maths topics are: 'Astro Physics', MegaHertz
Maths topics are: 'Differential Equations', Cosine Series
Integer value is 12
Pi value is 3.14159
Example for Any
Topics 'solid physics', Hertz

AnyObject

Para representar a instância de qualquer tipo de classe, a palavra-chave 'AnyObject' é usada.

class Subjects {
   var physics: String
   init(physics: String) {
      self.physics = physics
   }
}

class Chemistry: Subjects {
   var equations: String
   init(physics: String, equations: String) {
      self.equations = equations
      super.init(physics: physics)
   }
}

class Maths: Subjects {
   var formulae: String
   init(physics: String, formulae: String) {
      self.formulae = formulae
      super.init(physics: physics)
   }
}

let saprint: [AnyObject] = [Chemistry(physics: "solid physics", equations: "Hertz"),
   Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz"),
   Chemistry(physics: "Thermo physics", equations: "Decibels"),
   Maths(physics: "Astro Physics", formulae: "MegaHertz"),
   Maths(physics: "Differential Equations", formulae: "Cosine Series")]

let samplechem = Chemistry(physics: "solid physics", equations: "Hertz")
print("Instance physics is: \(samplechem.physics)")
print("Instance equation is: \(samplechem.equations)")

let samplemaths = Maths(physics: "Fluid Dynamics", formulae: "Giga Hertz")
print("Instance physics is: \(samplemaths.physics)")
print("Instance formulae is: \(samplemaths.formulae)")

var chemCount = 0
var mathsCount = 0

for item in saprint {
   if let print = item as? Chemistry {
      print("Chemistry topics are: '\(print.physics)', \(print.equations)")
   } else if let example = item as? Maths {
      print("Maths topics are: '\(example.physics)', \(example.formulae)")
   }
}

var exampleany = [Any]()
exampleany.append(12)
exampleany.append(3.14159)
exampleany.append("Example for Any")
exampleany.append(Chemistry(physics: "solid physics", equations: "Hertz"))

for print in exampleany {
   switch print {
      case let someInt as Int:
         print("Integer value is \(someInt)")
      case let someDouble as Double where someDouble > 0:
         print("Pi value is \(someDouble)")
      case let someString as String:
         print("\(someString)")
      case let phy as Chemistry:
         print("Topics '\(phy.physics)', \(phy.equations)")
      default:
         print("None")
   }
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Instance physics is: solid physics
Instance equation is: Hertz
Instance physics is: Fluid Dynamics
Instance formulae is: Giga Hertz
Chemistry topics are: 'solid physics', Hertz
Maths topics are: 'Fluid Dynamics', Giga Hertz
Chemistry topics are: 'Thermo physics', Decibels
Maths topics are: 'Astro Physics', MegaHertz
Maths topics are: 'Differential Equations', Cosine Series
Integer value is 12
Pi value is 3.14159
Example for Any
Topics 'solid physics', Hertz

A funcionalidade de uma classe, estrutura ou tipo de enumeração existente pode ser adicionada com a ajuda de extensões. A funcionalidade de tipo pode ser adicionada com extensões, mas não é possível substituir a funcionalidade com extensões.

Swift Extension Functionalities -

  • Adicionando propriedades computadas e propriedades de tipo computado
  • Definição de métodos de instância e tipo.
  • Fornecendo novos inicializadores.
  • Definindo subscritos
  • Definindo e usando novos tipos aninhados
  • Fazendo um tipo existente em conformidade com um protocolo

As extensões são declaradas com a palavra-chave 'extensão'

Sintaxe

extension SomeType {
   // new functionality can be added here
}

O tipo existente também pode ser adicionado com extensões para torná-lo um padrão de protocolo e sua sintaxe é semelhante à das classes ou estruturas.

extension SomeType: SomeProtocol, AnotherProtocol {
   // protocol requirements is described here
}

Propriedades Computadas

As propriedades computadas de 'instância' e 'tipo' também podem ser estendidas com a ajuda de extensões.

extension Int {
   var add: Int {return self + 100 }
   var sub: Int { return self - 10 }
   var mul: Int { return self * 10 }
   var div: Int { return self / 5 }
}

let addition = 3.add
print("Addition is \(addition)")

let subtraction = 120.sub
print("Subtraction is \(subtraction)")

let multiplication = 39.mul
print("Multiplication is \(multiplication)")

let division = 55.div
print("Division is \(division)")

let mix = 30.add + 34.sub
print("Mixed Type is \(mix)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Addition is 103
Subtraction is 110
Multiplication is 390
Division is 11
Mixed Type is 154

Inicializadores

O Swift 4 oferece flexibilidade para adicionar novos inicializadores a um tipo existente por meio de extensões. O usuário pode adicionar seus próprios tipos personalizados para estender os tipos já definidos e opções de inicialização adicionais também são possíveis. As extensões suportam apenas init (). deinit () não é suportado pelas extensões.

struct sum {
   var num1 = 100, num2 = 200
}

struct diff {
   var no1 = 200, no2 = 100
}

struct mult {
   var a = sum()
   var b = diff()
}

let calc = mult()
print ("Inside mult block \(calc.a.num1, calc.a.num2)")
print("Inside mult block \(calc.b.no1, calc.b.no2)")

let memcalc = mult(a: sum(num1: 300, num2: 500),b: diff(no1: 300, no2: 100))
print("Inside mult block \(memcalc.a.num1, memcalc.a.num2)")
print("Inside mult block \(memcalc.b.no1, memcalc.b.no2)")

extension mult {
   init(x: sum, y: diff) {
      let X = x.num1 + x.num2
      let Y = y.no1 + y.no2
   }
}

let a = sum(num1: 100, num2: 200)
print("Inside Sum Block:\( a.num1, a.num2)")

let b = diff(no1: 200, no2: 100)
print("Inside Diff Block: \(b.no1, b.no2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Inside mult block (100, 200)
Inside mult block (200, 100)
Inside mult block (300, 500)
Inside mult block (300, 100)
Inside Sum Block:(100, 200)
Inside Diff Block: (200, 100)

Métodos

Novos métodos de instância e métodos de tipo podem ser adicionados posteriormente à subclasse com a ajuda de extensões.

extension Int {
   func topics(summation: () -> ()) {
      for _ in 0..<self {
         summation()
      }
   }
}

4.topics(summation: {
   print("Inside Extensions Block")
})

3.topics(summation: {
   print("Inside Type Casting Block")
})

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Inside Extensions Block
Inside Extensions Block
Inside Extensions Block
Inside Extensions Block
Inside Type Casting Block
Inside Type Casting Block
Inside Type Casting Block

A função de tópicos () recebe um argumento do tipo '(soma: () → ())' para indicar que a função não aceita nenhum argumento e não retornará nenhum valor. Para chamar essa função várias vezes, for block é inicializado e a chamada ao método com topic () é inicializada.

Métodos de instância mutante

Os métodos de instância também podem sofrer mutação quando declarados como extensões.

Os métodos de estrutura e enumeração que modificam a si mesmo ou suas propriedades devem marcar o método de instância como mutante, assim como os métodos mutantes de uma implementação original.

extension Double {
   mutating func square() {
      let pi = 3.1415
      self = pi * self * self
   }
}

var Trial1 = 3.3
Trial1.square()
print("Area of circle is: \(Trial1)")

var Trial2 = 5.8
Trial2.square()
print("Area of circle is: \(Trial2)")

var Trial3 = 120.3
Trial3.square()
print("Area of circle is: \(Trial3)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Area of circle is: 34.210935
Area of circle is: 105.68006
Area of circle is: 45464.070735

Subscritos

Adicionar novos subscritos a instâncias já declaradas também pode ser possível com extensões.

extension Int {
   subscript(var multtable: Int) -> Int {
      var no1 = 1
      while multtable > 0 {
         no1 *= 10
         --multtable
      }
      return (self / no1) % 10
   }
}

print(12[0])
print(7869[1])
print(786543[2])

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

2
6
5

Tipos Aninhados

Tipos aninhados para instâncias de classe, estrutura e enumeração também podem ser estendidos com a ajuda de extensões.

extension Int {
   enum calc {
      case add
      case sub
      case mult
      case div
      case anything
   }
   var print: calc {
      switch self {
         case 0:
            return .add
         case 1:
            return .sub
         case 2:
            return .mult
         case 3:
            return .div
         default:
            return .anything
      }
   }
}

func result(numb: [Int]) {
   for i in numb {
      switch i.print {
         case .add:
            print(" 10 ")
         case .sub:
            print(" 20 ")
         case .mult:
            print(" 30 ")
         case .div:
            print(" 40 ")
         default:
            print(" 50 ")
      }
   }
}
result(numb: [0, 1, 2, 3, 4, 7])

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

10
20
30
40
50
50

Os protocolos fornecem um esquema para métodos, propriedades e outras funcionalidades de requisitos. É apenas descrito como um esqueleto de métodos ou propriedades em vez de implementação. A implementação de métodos e propriedades pode ser realizada definindo classes, funções e enumerações. A conformidade de um protocolo é definida como os métodos ou propriedades que satisfazem os requisitos do protocolo.

Sintaxe

Os protocolos também seguem a sintaxe semelhante à das classes, estruturas e enumerações -

protocol SomeProtocol {
   // protocol definition 
}

Os protocolos são declarados após os nomes da classe, estrutura ou tipo de enumeração. Declarações de protocolo único e múltiplo também são possíveis. Se vários protocolos forem definidos, eles devem ser separados por vírgulas.

struct SomeStructure: Protocol1, Protocol2 {
   // structure definition 
}

Quando um protocolo deve ser definido para superclasse, o nome do protocolo deve seguir o nome da superclasse com uma vírgula.

class SomeClass: SomeSuperclass, Protocol1, Protocol2 {
   // class definition 
}

Requisitos de propriedade e método

O protocolo é usado para especificar uma propriedade de tipo de classe particular ou propriedade de instância. Ele apenas especifica o tipo ou propriedade de instância sozinha, em vez de especificar se é uma propriedade armazenada ou computada. Além disso, é usado para especificar se a propriedade é 'gettable' ou 'setable'.

Os requisitos de propriedade são declarados pela palavra-chave 'var' como variáveis ​​de propriedade. {get set} é usado para declarar propriedades gettable e setable após sua declaração de tipo. Gettable é mencionado pela propriedade {get} após sua declaração de tipo.

protocol classa {
   var marks: Int { get set }
   var result: Bool { get }
   
   func attendance() -> String
   func markssecured() -> String
}

protocol classb: classa {
   var present: Bool { get set }
   var subject: String { get set }
   var stname: String { get set }
}

class classc: classb {
   var marks = 96
   let result = true
   var present = false
   var subject = "Swift 4 Protocols"
   var stname = "Protocols"

   func attendance() -> String {
      return "The \(stname) has secured 99% attendance"
   }
   func markssecured() -> String {
      return "\(stname) has scored \(marks)"
   }
}

let studdet = classc()
studdet.stname = "Swift 4"
studdet.marks = 98
studdet.markssecured()

print(studdet.marks)
print(studdet.result)
print(studdet.present)
print(studdet.subject)
print(studdet.stname)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

98
true
false
Swift 4 Protocols
Swift 4

Requisitos do método mutante

protocol daysofaweek {
   mutating func print()
}

enum days: daysofaweek {
   case sun, mon, tue, wed, thurs, fri, sat 
   mutating func print() {
      switch self {
         case sun:
            self = sun
            print("Sunday")
         case mon:
            self = mon
            print("Monday")
         case tue:
            self = tue
            print("Tuesday")
         case wed:
            self = wed
            print("Wednesday")
         case mon:
            self = thurs
            print("Thursday")
         case tue:
            self = fri
            print("Friday")
         case sat:
            self = sat
            print("Saturday")
         default:
            print("NO Such Day")
      }
   }
}

var res = days.wed
res.print()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Wednesday

Requisitos do inicializador

O Swing permite ao usuário inicializar protocolos para seguir o tipo de conformidade semelhante aos inicializadores normais.

Sintaxe

protocol SomeProtocol {
   init(someParameter: Int)
}

Por exemplo

protocol tcpprotocol {
   init(aprot: Int)
}

Implementações de classes dos requisitos do inicializador de protocolo

O inicializador designado ou de conveniência permite ao usuário inicializar um protocolo para estar em conformidade com seu padrão pela palavra-chave reservada 'necessária'.

class SomeClass: SomeProtocol {
   required init(someParameter: Int) {
      // initializer implementation statements
   }
}

protocol tcpprotocol {
   init(aprot: Int)
}

class tcpClass: tcpprotocol {
   required init(aprot: Int) {
   }
}

A conformidade do protocolo é garantida em todas as subclasses para implementação explícita ou herdada pelo modificador 'obrigatório'.

Quando uma subclasse sobrescreve seu requisito de inicialização de superclasse, ela é especificada pela palavra-chave modificadora 'override'.

protocol tcpprotocol {
   init(no1: Int)
}

class mainClass {
   var no1: Int        // local storage
   init(no1: Int) {
      self.no1 = no1  // initialization
   }
}

class subClass: mainClass, tcpprotocol {
   var no2: Int
   init(no1: Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   // Requires only one parameter for convenient method
   required override convenience init(no1: Int) {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

res is: 20
res is: 30
res is: 50

Protocolos como Tipos

Em vez de implementar funcionalidades em um protocolo, eles são usados ​​como tipos de funções, classes, métodos etc.

Os protocolos podem ser acessados ​​como tipos em -

  • Função, método ou inicialização como um parâmetro ou tipo de retorno

  • Constante, variável ou propriedade

  • Matrizes, dicionários ou outros recipientes como itens

protocol Generator {
   typealias members
   func next() -> members?
}

var items = [10,20,30].generate()
while let x = items.next() {
   print(x)
}

for lists in map([1,2,3], {i in i*5}) {
   print(lists)
}

print([100,200,300])
print(map([1,2,3], {i in i*10}))

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

10
20
30
5
10
15
[100, 200, 300]
[10, 20, 30]

Adicionando conformidade de protocolo com uma extensão

O tipo existente pode ser adotado e conformado a um novo protocolo, fazendo uso de extensões. Novas propriedades, métodos e subscritos podem ser adicionados aos tipos existentes com a ajuda de extensões.

protocol AgeClasificationProtocol {
   var age: Int { get }
   func agetype() -> String
}
class Person {
   let firstname: String
   let lastname: String
   var age: Int
   
   init(firstname: String, lastname: String) {
      self.firstname = firstname
      self.lastname = lastname
      self.age = 10
   }
}

extension Person : AgeClasificationProtocol {
   func fullname() -> String {
      var c: String
      c = firstname + " " + lastname
      return c
   }
   func agetype() -> String {
      switch age {
         case 0...2:
            return "Baby"
         case 2...12:
            return "Child"
         case 13...19:
            return "Teenager"
         case let x where x > 65:
            return "Elderly"
         default:
            return "Normal"
      }
   }
}

Herança de protocolo

O Swift 4 permite que os protocolos herdem propriedades de suas propriedades definidas. É semelhante ao da herança de classe, mas com a opção de listar vários protocolos herdados separados por vírgulas.

protocol classa {
   var no1: Int { get set }
   func calc(sum: Int)
}
protocol result {
   func print(target: classa)
}
class student2: result {
   func print(target: classa) {
      target.calc(sum: 1)
   }
}
class classb: result {
   func print(target: classa) {
      target.calc(sum: 5)
   }
}

class student: classa {
   var no1: Int = 10
   
   func calc(sum: Int) {
      no1 -= sum
      print("Student attempted \(sum) times to pass")
         
      if no1 <= 0 {
         print("Student is absent for exam")
      }
   }
}

class Player {
   var stmark: result!

   init(stmark: result) {
      self.stmark = stmark
   }
   func print(target: classa) {
      stmark.print(target: target)
   }
}

var marks = Player(stmark: student2())
var marksec = student()

marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)
marks.stmark = classb()
marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Student attempted 1 times to pass
Student attempted 1 times to pass
Student attempted 1 times to pass
Student attempted 5 times to pass
Student attempted 5 times to pass
Student is absent for exam
Student attempted 5 times to pass
Student is absent for exam

Protocolos somente de classe

Quando os protocolos são definidos e o usuário deseja definir o protocolo com classes, ele deve ser adicionado definindo a classe primeiro, seguida pela lista de herança do protocolo.

protocol tcpprotocol {
   init(no1: Int)
}
class mainClass {
   var no1: Int        // local storage
   init(no1: Int) {
      self.no1 = no1  // initialization
   }
}
class subClass: mainClass, tcpprotocol {
   var no2: Int
   init(no1: Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   
   // Requires only one parameter for convenient method
   required override convenience init(no1: Int) {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

res is: 20
res is: 30
res is: 50

Composição de Protocolo

O Swift 4 permite que vários protocolos sejam chamados de uma vez com a ajuda da composição do protocolo.

Sintaxe

protocol<SomeProtocol, AnotherProtocol>

Exemplo

protocol stname {
   var name: String { get }
}
protocol stage {
   var age: Int { get }
}
struct Person: stname, stage {
   var name: String
   var age: Int
}
func print(celebrator: stname & stage) {
   print("\(celebrator.name) is \(celebrator.age) years old")
}
let studname = Person(name: "Priya", age: 21)
print(studname)

let stud = Person(name: "Rehan", age: 29)
print(stud)

let student = Person(name: "Roshan", age: 19)
print(student)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Person(name: "Priya", age: 21)
Person(name: "Rehan", age: 29)
Person(name: "Roshan", age: 19)

Verificando a conformidade do protocolo

A conformidade do protocolo é testada pelos operadores 'is' e 'as' semelhantes ao da conversão de tipo.

  • O operador is retorna verdadeiro se uma instância estiver em conformidade com o padrão do protocolo e retorna falso se falhar.

  • o as? version do operador downcast retorna um valor opcional do tipo do protocolo, e esse valor é nulo se a instância não estiver em conformidade com aquele protocolo.

  • A versão as do operador downcast força o downcast para o tipo de protocolo e dispara um erro de tempo de execução se o downcast não for bem-sucedido.

import Foundation

@objc protocol rectangle {
   var area: Double { get }
}
@objc class Circle: rectangle {
   let pi = 3.1415927
   var radius: Double
   var area: Double { return pi * radius * radius }
   init(radius: Double) { self.radius = radius }
}
@objc class result: rectangle {
   var area: Double
   init(area: Double) { self.area = area }
}
class sides {
   var rectsides: Int
   init(rectsides: Int) { self.rectsides = rectsides }
}
let objects: [AnyObject] = [Circle(radius: 2.0),result(area:198),sides(rectsides: 4)]

for object in objects {
   if let objectWithArea = object as? rectangle {
      print("Area is \(objectWithArea.area)")
   } else {
      print("Rectangle area is not defined")
   }
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Area is 12.5663708
Area is 198.0
Rectangle area is not defined

A linguagem Swift 4 fornece recursos 'genéricos' para escrever funções e tipos flexíveis e reutilizáveis. Os genéricos são usados ​​para evitar duplicação e fornecer abstração. As bibliotecas padrão do Swift 4 são construídas com código genérico. Os tipos 'Arrays' e 'Dicionário' do Swift 4s pertencem a coleções genéricas. Com a ajuda de arrays e dicionários, os arrays são definidos para conter valores 'Int' e valores 'String' ou quaisquer outros tipos.

func exchange(a: inout Int, b: inout Int) {
   let temp = a
   a = b
   b = temp
}

var numb1 = 100
var numb2 = 200

print("Before Swapping values are: \(numb1) and \(numb2)")
exchange(a: &numb1, b: &numb2)
print("After Swapping values are: \(numb1) and \(numb2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Before Swapping values are: 100 and 200
After Swapping values are: 200 and 100

Funções genéricas: parâmetros de tipo

Funções genéricas podem ser usadas para acessar qualquer tipo de dados como 'Int' ou 'String'.

func exchange<T>(a: inout T, b: inout T) {
   let temp = a
   a = b
   b = temp
}
var numb1 = 100
var numb2 = 200

print("Before Swapping Int values are: \(numb1) and \(numb2)")
exchange(a: &numb1, b: &numb2)
print("After Swapping Int values are: \(numb1) and \(numb2)")

var str1 = "Generics"
var str2 = "Functions"

print("Before Swapping String values are: \(str1) and \(str2)")
exchange(a: &str1, b: &str2)
print("After Swapping String values are: \(str1) and \(str2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Before Swapping Int values are: 100 and 200
After Swapping Int values are: 200 and 100
Before Swapping String values are: Generics and Functions
After Swapping String values are: Functions and Generics

A função exchange () é usada para trocar valores que são descritos no programa acima e <T> é usado como um parâmetro de tipo. Pela primeira vez, a função exchange () é chamada para retornar valores 'Int' e a segunda chamada para a função exchange () retornará valores 'String'. Vários tipos de parâmetros podem ser incluídos dentro dos colchetes angulares separados por vírgulas.

Os parâmetros de tipo são nomeados como definidos pelo usuário para saber a finalidade do parâmetro de tipo que ele contém. Swift 4 fornece <T> como nome de parâmetro de tipo genérico. No entanto, parâmetros de tipo como Arrays e Dicionários também podem ser nomeados como chave, valor para identificar que eles pertencem ao tipo 'Dicionário'.

struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
}

var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)

let deletetos = tos.pop()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Type Parameters]
[Swift 4, Generics, Type Parameters, Naming Type Parameters]

Estendendo um tipo genérico

Estender a propriedade stack para saber que o topo do item está incluído com a palavra-chave 'extension'.

struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
}
var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)

extension TOS {
   var first: T? {
      return items.isEmpty ? nil : items[items.count - 1]
   }
}
if let first = tos.first {
   print("The top item on the stack is \(first).")
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

["Swift 4"]
["Swift 4", "Generics"]
["Swift 4", "Generics", "Type Parameters"]
["Swift 4", "Generics", "Type Parameters", "Naming Type Parameters"]
The top item on the stack is Naming Type Parameters.

Restrições de tipo

A linguagem Swift 4 permite 'restrições de tipo' para especificar se o parâmetro de tipo herda de uma classe específica ou para garantir o padrão de conformidade do protocolo.

func exchange<T>(a: inout T, b: inout T) {
   let temp = a
   a = b
   b = temp
}
var numb1 = 100
var numb2 = 200

print("Before Swapping Int values are: \(numb1) and \(numb2)")
exchange(a: &numb1, b: &numb2)
print("After Swapping Int values are: \(numb1) and \(numb2)")

var str1 = "Generics"
var str2 = "Functions"

print("Before Swapping String values are: \(str1) and \(str2)")
exchange(a: &str1, b: &str2)
print("After Swapping String values are: \(str1) and \(str2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Before Swapping Int values are: 100 and 200
After Swapping Int values are: 200 and 100
Before Swapping String values are: Generics and Functions
After Swapping String values are: Functions and Generics

Tipos Associados

O Swift 4 permite que os tipos associados sejam declarados dentro da definição do protocolo pela palavra-chave 'associatedtype'.

protocol Container {
   associatedtype ItemType
   mutating func append(item: ItemType)
   var count: Int { get }
   subscript(i: Int) -> ItemType { get }
}
struct TOS<T>: Container {
   // original Stack<T> implementation
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
   
   // conformance to the Container protocol
   mutating func append(item: T) {
      self.push(item: item)
   }
   var count: Int {
      return items.count
   }
   subscript(i: Int) -> T {
      return items[i]
   }
}
var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Type Parameters]
[Swift 4, Generics, Type Parameters, Naming Type Parameters]

Onde cláusulas

As restrições de tipo permitem que o usuário defina requisitos nos parâmetros de tipo associados a uma função ou tipo genérico. Para definir requisitos para tipos associados, as cláusulas 'where' são declaradas como parte da lista de parâmetros de tipo. A palavra-chave 'where' é colocada imediatamente após a lista de parâmetros de tipo seguidos por restrições de tipos associados, relações de igualdade entre tipos e tipos associados.

protocol Container {
   associatedtype ItemType
   mutating func append(item: ItemType)
   var count: Int { get }
   subscript(i: Int) -> ItemType { get }
}
struct Stack<T>: Container {
   // original Stack<T> implementation
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }

   // conformance to the Container protocol
   mutating func append(item: T) {
      self.push(item: item)
   }
   var count: Int {
      return items.count
   }
   subscript(i: Int) -> T {
      return items[i]
   }
}
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer: C2) -> Bool {
   // check that both containers contain the same number of items
   if someContainer.count != anotherContainer.count {
      return false
   }
   
   // check each pair of items to see if they are equivalent
   for i in 0..<someContainer.count {
      if someContainer[i] != anotherContainer[i] {
         return false
      }
   }
   // all items match, so return true
   return true
}  
var tos = Stack<String>()

tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Where Clause")
print(tos.items)

var eos = ["Swift 4", "Generics", "Where Clause"]
print(eos)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Where Clause]
[Swift 4, Generics, Where Clause]

Para restringir o acesso aos blocos de código, módulos e abstração é feito através do controle de acesso. Classes, estruturas e enumerações podem ser acessadas de acordo com suas propriedades, métodos, inicializadores e subscritos por mecanismos de controle de acesso. Constantes, variáveis ​​e funções em um protocolo são restritas e têm acesso permitido como global e local por meio do controle de acesso. O controle de acesso aplicado a propriedades, tipos e funções pode ser referido como 'entidades'.

O modelo de controle de acesso é baseado em módulos e arquivos fonte.

O módulo é definido como uma unidade única de distribuição de código e pode ser importado usando a palavra-chave 'importar'. Um arquivo de origem é definido como um único arquivo de código-fonte com um módulo para acessar vários tipos e funções.

Três níveis de acesso diferentes são fornecidos pela linguagem Swift 4. Eles são de acesso público, interno e privado.

S.No Níveis de acesso e definição
1

Public

Permite que as entidades sejam processadas em qualquer arquivo de origem de seu módulo de definição, um arquivo de origem de outro módulo que importa o módulo de definição.

2

Internal

Permite que entidades sejam usadas em qualquer arquivo de origem de seu módulo de definição, mas não em qualquer arquivo de origem fora desse módulo.

3

Private

Restringe o uso de uma entidade a seu próprio arquivo de definição de origem. O acesso privado desempenha o papel de ocultar os detalhes de implementação de uma funcionalidade de código específica.

Sintaxe

public class SomePublicClass {}
internal class SomeInternalClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
private func somePrivateFunction() {}

Controle de acesso para tipos de função

Algumas funções podem ter argumentos declarados dentro da função sem nenhum valor de retorno. O programa a seguir declara aeb como argumentos para a função sum (). Dentro da própria função, os valores dos argumentos aeb são passados ​​invocando a chamada de função sum () e seus valores são impressos, eliminando assim os valores de retorno. Para tornar o tipo de retorno da função privado, declare o nível de acesso geral da função com o modificador privado.

private func sum(a: Int, b: Int) {
   let a = a + b
   let b = a - b
   print(a, b)
}

sum(a: 20, b: 10)
sum(a: 40, b: 10)
sum(a: 24, b: 6)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

30 20
50 40
30 24

Controle de acesso para tipos de enumeração

public enum Student {
   case Name(String)
   case Mark(Int,Int,Int)
}
var studDetails = Student.Name("Swift 4")
var studMarks = Student.Mark(98,97,95)

switch studMarks {
   case .Name(let studName):
      print("Student name is: \(studName).")
   case .Mark(let Mark1, let Mark2, let Mark3):
      print("Student Marks are: \(Mark1),\(Mark2),\(Mark3).")
   
}

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Student Marks are: 98,97,95

A enumeração na linguagem Swift 4 recebe automaticamente o mesmo nível de acesso para casos individuais de uma enumeração. Considere, por exemplo, acessar o nome do aluno e as marcas garantidas em três disciplinas. O nome da enumeração é declarado como aluno e os membros presentes na classe enum são o nome que pertence ao tipo de dados da string, as marcas são representadas como mark1, mark2 e mark3 do tipo de dados Inteiro. Para acessar o nome do aluno ou as notas que ele pontuou. Agora, o caso de troca imprimirá o nome do aluno se esse bloco de caso for executado, caso contrário, ele imprimirá as marcas garantidas pelo aluno. Se ambas as condições falharem, o bloco padrão será executado.

Controle de acesso para subclasses

O Swift 4 permite ao usuário criar uma subclasse de qualquer classe que possa ser acessada no contexto de acesso atual. Uma subclasse não pode ter um nível de acesso mais alto do que sua superclasse. O usuário não pode escrever uma subclasse pública de uma superclasse interna.

public class cricket {
   internal func printIt() {
      print("Welcome to Swift 4 Super Class")
   }
}

internal class tennis: cricket {
   override internal func printIt() {
      print("Welcome to Swift 4 Sub Class")
   }
}

let cricinstance = cricket()
cricinstance.printIt()

let tennisinstance = tennis()
tennisinstance.printIt()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Welcome to Swift Super Class
Welcome to Swift Sub Class

Controle de acesso para constantes, variáveis, propriedades e subscritos

A constante, variável ou propriedade do Swift 4 não pode ser definida como pública do que seu tipo. Não é válido escrever uma propriedade pública com um tipo privado. Da mesma forma, um subscrito não pode ser mais público do que seu índice ou tipo de retorno.

Quando uma constante, variável, propriedade ou subscrito faz uso de um tipo privado, a constante, variável, propriedade ou subscrito também deve ser marcada como privada -

private var privateInstance = SomePrivateClass()

Getters e Setters

Os getters e setters de constantes, variáveis, propriedades e subscritos recebem automaticamente o mesmo nível de acesso da constante, variável, propriedade ou subscrito a que pertencem.

class Samplepgm {
   var counter: Int = 0{
      willSet(newTotal) {
         print("Total Counter is: \(newTotal)")
      }
      didSet {
         if counter > oldValue {
            print("Newly Added Counter \(counter - oldValue)")
         }
      }
   }
}

let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

Total Counter is: 100
Newly Added Counter 100
Total Counter is: 800
Newly Added Counter 700

Controle de acesso para inicializadores e inicializadores padrão

Os inicializadores personalizados podem ser atribuídos a um nível de acesso menor ou igual ao tipo que inicializam. Um inicializador necessário deve ter o mesmo nível de acesso que a classe a que pertence. Os tipos de parâmetros de um inicializador não podem ser mais privados do que o nível de acesso do próprio inicializador.

Para declarar toda e qualquer subclasse da palavra-chave initialize 'required' precisa ser definida antes da função init ().

class classA {
   required init() {
      let a = 10
      print(a)
   }
}
class classB: classA {
   required init() {
      let b = 30
      print(b)
   }
}
let res = classA()
let print = classB()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

10
30
10

Um inicializador padrão tem o mesmo nível de acesso do tipo que inicializa, a menos que esse tipo seja definido como público. Quando a inicialização padrão é definida como pública, ela é considerada interna. Quando o usuário precisa que um tipo público seja inicializável com um inicializador sem argumento em outro módulo, forneça explicitamente um inicializador sem argumento público como parte da definição do tipo.

Controle de acesso para protocolos

Quando definimos um novo protocolo para herdar funcionalidades de um protocolo existente, ambos devem ser declarados com os mesmos níveis de acesso para herdar as propriedades um do outro. O controle de acesso do Swift 4 não permite que os usuários definam um protocolo 'público' que herda de um protocolo 'interno'.

public protocol tcpprotocol {
   init(no1: Int)
}
public class mainClass {
   var no1: Int      // local storage
   init(no1: Int) {
      self.no1 = no1 // initialization
   }
}
class subClass: mainClass, tcpprotocol {
   var no2: Int
   init(no1: Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   
   // Requires only one parameter for convenient method
   required override convenience init(no1: Int) {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

res is: 20
res is: 30
res is: 50

Controle de acesso para extensões

O Swift 4 não permite que os usuários forneçam um modificador de nível de acesso explícito para uma extensão quando o usuário usa essa extensão para adicionar conformidade de protocolo. O nível de acesso padrão para cada implementação de requisito de protocolo na extensão é fornecido com seu próprio nível de acesso de protocolo.

Controle de acesso para genéricos

Os genéricos permitem que o usuário especifique níveis mínimos de acesso para acessar as restrições de tipo em seus parâmetros de tipo.

public struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
}

var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)
let deletetos = tos.pop()

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Type Parameters]
[Swift 4, Generics, Type Parameters, Naming Type Parameters]

Controle de acesso para aliases de tipo

O usuário pode definir aliases de tipo para tratar tipos de controle de acesso distintos. O mesmo nível de acesso ou diferentes níveis de acesso podem ser definidos pelo usuário. Quando o alias do tipo é 'privado', seus membros associados podem ser declarados como 'privado, interno do tipo público'. Quando o alias do tipo é público, os membros não podem ser alias como um nome 'interno' ou 'privado'

Quaisquer apelidos de tipo que você definir são tratados como tipos distintos para fins de controle de acesso. Um alias de tipo pode ter um nível de acesso menor ou igual ao nível de acesso do tipo que ele atribui. Por exemplo, um alias de tipo privado pode ser um alias de um tipo privado, interno ou público, mas um alias de tipo público não pode ser um alias de um tipo interno ou privado.

public protocol Container {
   associatedtype ItemType
   mutating func append(item: ItemType)
   var count: Int { get }
   subscript(i: Int) -> ItemType { get }
}
struct Stack<T>: Container {
   // original Stack<T> implementation
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
   
   // conformance to the Container protocol
   mutating func append(item: T) {
      self.push(item: item)
   }
   var count: Int {
      return items.count
   }
   subscript(i: Int) -> T {
      return items[i]
   }
}
func allItemsMatch<
   C1: Container, C2: Container
   where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
   (someContainer: C1, anotherContainer: C2) -> Bool {
   
   // check that both containers contain the same number of items
   if someContainer.count != anotherContainer.count {
      return false
   }
   
   // check each pair of items to see if they are equivalent
   for i in 0..<someContainer.count {
      if someContainer[i] != anotherContainer[i] {
         return false
      }
   }
   // all items match, so return true
   return true
}
var tos = Stack<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Where Clause")
print(tos.items)

var eos = ["Swift 4", "Generics", "Where Clause"]
print(eos)

Quando executamos o programa acima usando playground, obtemos o seguinte resultado -

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Where Clause]
[Swift 4, Generics, Where Clause]

Codificação e decodificação Swift

Swift 4 apresenta um novo Codable Protocolo, que permite serializar e desserializar tipos de dados personalizados sem escrever nenhum código especial - e sem ter que se preocupar em perder seus tipos de valor.

struct Language: Codable {
   var name: String
   var version: Int
}
let swift = Language(name: "Swift", version: 4)
let java = Language(name: "java", version: 8)
let R = Language(name: "R", version: 3

Observe que Langauage está em conformidade com o protocolo codificável. Agora vamos convertê-lo em uma representação de dados Json usando uma linha simples.

let encoder = JSONEncoder()
if let encoded = try? encoder.encode(java) {
   //Perform some operations on this value.
}

O Swift codificará automaticamente todos os valores dentro do seu tipo de dados.

Você pode decodificar os dados usando a função de decodificador como

let decoder = JSONDecoder()
if let decoded = try? decoder.decode(Language.self, from: encoded) {
   //Perform some operations on this value.
}

Ambos JSONEncoder e sua contraparte de lista de propriedades PropertyListEncoder têm muitas opções para personalizar a forma como funcionam.