A linguagem de programação 
Swift tem apenas quatro anos, mas já está se tornando a principal linguagem de desenvolvimento para iOS. Desenvolvendo para a versão 5.0, o Swift se tornou uma linguagem complexa e poderosa que atende tanto a paradigmas orientados a objetos quanto a funcionais. E a cada nova versão, ele adiciona ainda mais recursos.
Mas quão bem você 
realmente conhece Swift? Neste artigo, você encontrará exemplos de perguntas para uma entrevista Swift.
Você pode usar essas perguntas para entrevistar os candidatos para testar seus conhecimentos ou testar seus próprios conhecimentos. Se você não souber a resposta, não se preocupe: há uma resposta para cada pergunta.
As perguntas são divididas em três grupos:
- Iniciante : para iniciantes. Você leu alguns livros e aplicou o Swift em seus próprios aplicativos.
- Intermediário : adequado para quem está realmente interessado no idioma. Você já leu muito sobre isso e frequentemente experimenta.
- Avançado : adequado para os desenvolvedores mais avançados - aqueles que gostam de entrar na selva de sintaxe e usar técnicas avançadas.
Existem dois tipos de perguntas para cada nível:
- escrito : adequado para testes por e-mail, pois sugerem a escrita de código.
- oral : pode ser usado ao falar ao telefone ou pessoalmente, pois há respostas suficientes em palavras.
Ao ler este artigo, mantenha o 
playground aberto para poder verificar o código da pergunta. Todas as respostas foram testadas no 
Xcode 10.2 e 
Swift 5 .
- Iniciante
 - perguntas escritas- Pergunta 1- Considere o seguinte código: - struct Tutorial { var difficulty: Int = 1 } var tutorial1 = Tutorial() var tutorial2 = tutorial1 tutorial2.difficulty = 2
 - Quais são os valores de  tutorial1.dificuldade-  e  tutorial2.dificuldade-  ? Haveria alguma diferença se o  Tutorial-  fosse uma aula? Porque - a resposta- tutorial1.dificuldade é 1 e tutorial2.dificuldade é 2. - No Swift, estruturas são tipos de valor. Eles são copiados, não referenciados. A linha a seguir copia o  tutorial1-  e o atribui ao  tutorial2-  : -  - var tutorial2 = tutorial1
 - Alterações no  tutorial2-  não afetam o  tutorial1-  . - Se Tutorial fosse uma classe,  tutorial1.dificuldade-  e  tutorial2.dificuldade-  seriam iguais a 2. Classes no Swift são tipos de referência. Quando você altera a propriedade tutorial1, verá a mesma alteração no tutorial2 - e vice-versa. 
 
 - Questão 2- Você declarou  view1-  com  var-  e  view2-  com  let-  . Qual é a diferença e a última linha é compilada? -  - import UIKit var view1 = UIView() view1.alpha = 0.5 let view2 = UIView() view2.alpha = 0.5 
 - a resposta- Sim, a última linha é compilada.  view1-  é uma variável e você pode atribuir seu valor a uma nova instância do UIView. Usando  let-  , você pode atribuir um valor apenas uma vez, para que o seguinte código não seja compilado: -  - view2 = view1 
 - No entanto, o UIView é uma classe com semântica referencial, portanto, você pode alterar as propriedades do view2 - o que significa que o código será  compilado-  . 
 
 - Pergunta 3- Esse código classifica a matriz em ordem alfabética. Simplifique o fechamento, tanto quanto possível. -  - var animals = ["fish", "cat", "chicken", "dog"] animals.sort { (one: String, two: String) -> Bool in return one < two } print(animals)
 - a resposta- O Swift  determina automaticamente o-  tipo de parâmetros de fechamento e o tipo de retorno, para que você possa  removê-los-  : -  - animals.sort { (one, two) in return one < two }
 - Você pode substituir os nomes de parâmetros usando a notação  $ i-  : -  - animals.sort { return $0 < $1 }
 - Os fechamentos que consistem em uma única instrução podem não conter a palavra-chave  return-  . O valor da última instrução executada se torna o resultado do retorno do fechamento: -  - animals.sort { $0 < $1 }
 - Por fim, como Swift sabe que os elementos da matriz estão em conformidade com o protocolo  Equatable-  , você pode simplesmente escrever: -  - animals.sort(by: <)
 - Upd:  hummingbirddj-  simplificou ainda mais: - Nesse caso, você pode ainda mais curto:-  -  animals.sort()
 - Classifica em ordem crescente, funciona para tipos que implementam Comparável.
 
 
 - Pergunta 4- Este código cria duas classes:  Endereço-  e  Pessoa-  . Também são criadas duas instâncias da classe  Person-  (  Ray-  e  Brian-  ). -  - class Address { var fullAddress: String var city: String init(fullAddress: String, city: String) { self.fullAddress = fullAddress self.city = city } } class Person { var name: String var address: Address init(name: String, address: Address) { self.name = name self.address = address } } var headquarters = Address(fullAddress: "123 Tutorial Street", city: "Appletown") var ray = Person(name: "Ray", address: headquarters) var brian = Person(name: "Brian", address: headquarters)
 - Suponha que  Brian tenha-  se mudado para um novo endereço e você queira atualizar o registro dele da seguinte maneira: -  - brian.address.fullAddress = "148 Tutorial Street"
 - Isso compila e executa sem erros. Mas, se você verificar agora o endereço de  Ray-  , verá que ele também  "se mudou"-  . - O que aconteceu aqui e como podemos corrigi-lo? - a resposta- O endereço é uma classe e tem semântica de referência . Assim, sede é a mesma instância da classe compartilhada por ray e brian. Mudar a sede mudará o endereço de ambos.
 Para corrigir isso, você pode criar uma nova instância da classe Address e atribuí-la a Brian, ou declarar Address como uma estrutura em vez de uma classe .
 
 
 
 
 
 - perguntas orais- Pergunta 1- O que é  opcional-  e quais problemas eles resolvem? - a resposta- opcional permite que uma variável de qualquer tipo apresente uma situação " sem valor ". No Objetivo-C, "nenhum valor" estava disponível apenas em tipos de referência usando o valor nulo especial. Tipos de valor, como int ou float , não tinham esse recurso.
 Swift estendeu o conceito de "sem valor" para tipos de valor. A variável opcional pode conter um valor ou nulo , indicando a ausência de um valor.
 
 
 
 - Questão 2- Liste brevemente as principais diferenças entre  estrutura-  e  classe-  . - a resposta- Classes suportam herança, mas estruturas não.
 Classes são um tipo de referência, estruturas são um tipo de valor.
 
 
 
 - Pergunta 3- O que são  genéricos-  e para que servem? - a resposta- No Swift, você pode usar  genéricos-  em classes, estruturas e enumerações. Os genéricos-  corrigem o problema da duplicação de código. Se você possui um método que aceita parâmetros de um tipo, às vezes é necessário duplicar o código para trabalhar com parâmetros de outro tipo. - Por exemplo, neste código, a segunda função é um "clone" da primeira, exceto que ela possui parâmetros de sequência, não inteiros. -  - func areIntEqual(_ x: Int, _ y: Int) -> Bool { return x == y } func areStringsEqual(_ x: String, _ y: String) -> Bool { return x == y } areStringsEqual("ray", "ray") 
 - Usando genéricos, você combina duas funções em uma e ao mesmo tempo garante a segurança do tipo: -  - func areTheyEqual<T: Equatable>(_ x: T, _ y: T) -> Bool { return x == y } areTheyEqual("ray", "ray") areTheyEqual(1, 1)
 - Como você está testando a igualdade, está restringindo tipos àqueles que estão em conformidade com o protocolo  Equatable-  . Este código fornece o resultado desejado e impede a transferência de parâmetros do tipo errado. 
 
 - Pergunta 4- Em alguns casos, não será possível evitar os opcionais implicitamente  desembrulhados-  . Quando e porque? - a resposta- Os motivos mais comuns para usar opcionais implicitamente desembrulhados são: - quando você não pode inicializar uma propriedade que não é nula no momento da criação. Um exemplo típico é a saída no Interface Builder, que é sempre inicializada após o proprietário. Nesse caso especial, se tudo estiver configurado corretamente no Interface Builder, você garante que a tomada não é nula antes de usá-la.
- para resolver o problema do loop de referências fortes , quando duas instâncias de classes se referem uma à outra e é necessária uma referência não nula para outra instância. Nesse caso, você marca o link de um lado como não proprietário e, do outro lado, usa expansão opcional implícita.
 
 
 - Questão 5- Quais são algumas maneiras de implantar opcional? Classifique-os em termos de segurança. -  - var x : String? = "Test"
 - Dica: apenas 7 maneiras. - a resposta- O desembrulhamento forçado-  não é seguro. -  - let a: String = x!
 A implantação implícita ao declarar uma variável-  é insegura. -  - var a = x!
 A ligação opcional-  é segura. -  - if let a = x { print("x was successfully unwrapped and is = \(a)") }
 O encadeamento opcional-  é seguro. -  - let a = x?.count
 Nenhum operador coalescente-  é seguro. -  - let a = x ?? ""
 - A declaração da  Guarda-  está segura. -  - guard let a = x else { return }
 Padrão opcional-  - seguro. -  - if case let a? = x { print(a) }
 
 
 
 
 
 
- Intermediário
 - perguntas escritas- Pergunta 1- Qual é a diferença entre  nil-  e  .none-  ? - a resposta- Não há diferença,  Optional.none-  (brevemente  .none-  ) e  nil são-  equivalentes. - De fato, a seguinte declaração retornará  true-  : -  - nil == .none
 - O uso de  zero é-  geralmente aceito e recomendado. 
 
 - Questão 2- Aqui está um modelo de termômetro na forma de classe e estrutura. O compilador reclama da última linha. O que há de errado aí? -  - public class ThermometerClass { private(set) var temperature: Double = 0.0 public func registerTemperature(_ temperature: Double) { self.temperature = temperature } } let thermometerClass = ThermometerClass() thermometerClass.registerTemperature(56.0) public struct ThermometerStruct { private(set) var temperature: Double = 0.0 public mutating func registerTemperature(_ temperature: Double) { self.temperature = temperature } } let thermometerStruct = ThermometerStruct() thermometerStruct.registerTemperature(56.0)
 - a resposta- ThermometerStruct está declarado corretamente com uma função de mutação para alterar a variável interna. O compilador reclama que você está chamando o método registerTemperature da instância que foi criada com let , portanto, essa instância é imutável. Alterar let para var corrigirá o erro de compilação.
 
 Nas estruturas, você deve marcar métodos que modificam variáveis internas como mutantes , mas não pode invocar esses métodos usando uma instância imutável.
 
 
 
 - Pergunta 3- O que esse código produzirá e por quê? -  - var thing = "cars" let closure = { [thing] in print("I love \(thing)") } thing = "airplanes" closure()
 - a resposta- Será impresso: eu amo carros. A lista de captura criará uma cópia da variável quando o fechamento for declarado. Isso significa que a variável capturada não alterará seu valor, mesmo após a atribuição de um novo valor. - Se você omitir a lista de captura no fechamento, o compilador usará o link, não a cópia. A chamada de fechamento refletirá a alteração na variável: -  - var thing = "cars" let closure = { print("I love \(thing)") } thing = "airplanes" closure() 
 
 
 - Pergunta 4- Esta é uma função que conta o número de valores exclusivos em uma matriz: -  - func countUniques<T: Comparable>(_ array: Array<T>) -> Int { let sorted = array.sorted() let initial: (T?, Int) = (.none, 0) let reduced = sorted.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) } return reduced.1 }
 - Ele usa classificados, portanto, usa apenas tipos que estão em conformidade com o protocolo Comparável. - Você pode chamar assim: -  - countUniques([1, 2, 3, 3]) 
 - Reescreva esta função como uma extensão Array para que você possa usá-la assim: -  - [1, 2, 3, 3].countUniques() 
 - nota do tradutor- Algo muito doloroso é uma função monstruosa. Porque não: -  - func countUniques<T: Hashable>(_ array: Array<T>) -> Int { return Set(array).count }
 
 - a resposta-  - extension Array where Element: Comparable { func countUniques() -> Int { let sortedValues = sorted() let initial: (Element?, Int) = (.none, 0) let reduced = sortedValues.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) } return reduced.1 } }
 
 
 - Questão 5- Aqui está uma função que divide duas duplas opcionais. Existem três condições que devem ser atendidas: - dividendo não deve ser nulo
- o divisor não deve ser nulo
- o divisor não deve ser 0
 -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { if dividend == nil { return nil } if divisor == nil { return nil } if divisor == 0 { return nil } return dividend! / divisor! }
 - Reescreva esta função usando a declaração de  guarda-  e não usando o desembrulho forçado. - a resposta- A declaração de  guarda-  introduzida no Swift 2.0 fornece uma saída se a condição não for atendida. Aqui está um exemplo: -  - guard dividend != nil else { return nil }
 - Você também pode usar a instrução guard para  ligação opcional-  , após o qual a variável expandida estará disponível  fora da instrução guard-  : -  - guard let dividend = dividend else { return .none }
 - Então você pode reescrever a função assim: -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { guard let dividend = dividend else { return nil } guard let divisor = divisor else { return nil } guard divisor != 0 else { return nil } return dividend / divisor }
 - Observe a falta de descompactação forçada, pois já descompactamos o dividendo e o divisor e os colocamos em variáveis imutáveis não opcionais. - Observe também que o resultado dos opcionais descompactados na declaração de guarda também está disponível fora da declaração de guarda. - Você pode simplificar ainda mais a função agrupando instruções de guarda: -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { guard let dividend = dividend, let divisor = divisor, divisor != 0 else { return nil } return dividend / divisor }
 
 
 - Pergunta 6- Reescreva o método da pergunta 5 usando a  instrução if let-  . - a resposta- A instrução if let permite descompactar opcionais e usar esse valor dentro deste bloco de código. Fora dele, esses valores não estarão disponíveis. -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { if let dividend = dividend, let divisor = divisor, divisor != 0 { return dividend / divisor } else { return nil } }
 
 
 
 
 - perguntas orais- Pergunta 1- No Objective-C, você declara uma constante assim: -  - const int number = 0;
 - E assim em Swift: -  - let number = 0
 - Qual a diferença? - a resposta- No  Objective-C, uma-  constante é inicializada  no tempo de compilação com um-  valor que deve ser conhecido neste momento. - Um valor imutável criado com  let-  é uma constante definida  em tempo de execução-  . Você pode inicializá-lo com uma expressão estática ou dinâmica. Portanto, podemos fazer isso: -  - let higherNumber = number + 5
 - Observe que essa tarefa só pode ser feita uma vez. 
 
 - Questão 2- Para declarar uma propriedade ou função estática para tipos de valor, o modificador  estático-  é usado. Aqui está um exemplo para a estrutura: -  - struct Sun { static func illuminate() {} }
 - E para as classes é possível usar modificadores  estáticos-  ou de  classe-  . O resultado é o mesmo, mas há uma diferença. Descreva-o. - a resposta- static-  torna uma propriedade ou função estática e  sem sobreposição-  . O uso da classe  substituirá uma-  propriedade ou função. - Aqui, o compilador jurará na tentativa de substituir o  illuminate ()-  : -  - class Star { class func spin() {} static func illuminate() {} } class Sun : Star { override class func spin() { super.spin() } 
 
 
 
 - Pergunta 3- É possível adicionar uma  propriedade armazenada-  a um tipo usando a  extensão-  ? Como ou por que não? - a resposta- Não, isso não é possível. Podemos usar a extensão para adicionar um novo comportamento a um tipo existente, mas não podemos alterar o tipo em si ou sua interface. Para armazenar a nova propriedade armazenada, precisamos de memória adicional e a extensão não pode fazer isso.
 
 
 
 - Pergunta 4- O que é um protocolo no Swift? - a resposta- Um protocolo é um tipo que define um esboço de métodos, propriedades etc. Uma classe, estrutura ou enumeração pode usar um protocolo para implementar tudo isso. O protocolo em si não implementa a funcionalidade, mas a define.
 
 
 
 
 
 
- Avançado
 - perguntas escritas- Pergunta 1- Suponha que tenhamos uma estrutura que define o modelo de um termômetro: -  - public struct Thermometer { public var temperature: Double public init(temperature: Double) { self.temperature = temperature } }
 - Para criar uma instância, escrevemos: -  - var t: Thermometer = Thermometer(temperature:56.8)
 - Mas algo assim seria muito mais conveniente: -  - var thermometer: Thermometer = 56.8
 - Isso é possível? Como - a resposta- Swift define protocolos que permitem inicializar um tipo usando literais por atribuição. Aplicar o protocolo apropriado e fornecer um inicializador público permitirá a inicialização usando literais. No caso do termômetro, implementamos  ExpressibleByFloatLiteral-  : -  - extension Thermometer: ExpressibleByFloatLiteral { public init(floatLiteral value: FloatLiteralType) { self.init(temperature: value) } }
 - Agora podemos criar uma instância como esta: -  - var thermometer: Thermometer = 56.8
 
 
 - Questão 2- Swift tem um conjunto de operadores predefinidos para operações aritméticas e lógicas. Também permite que você crie seus próprios operadores, unários e binários. - Defina e implemente seu próprio operador de exponenciação (^^) de acordo com os seguintes requisitos: - leva dois int como parâmetros
- retorna o resultado do aumento do primeiro parâmetro para a potência do segundo
- processa corretamente a ordem das operações algébricas
- ignora possíveis erros de estouro
 - a resposta- A criação de um novo operador ocorre em dois estágios: anúncio e implementação. - A declaração usa a palavra-chave  operator-  para especificar o tipo (unário ou binário), para especificar a sequência de caracteres do novo operador, sua associatividade e antiguidade de execução. - Aqui o operador é ^^ e seu tipo é infix (binário). Associatividade está certa. - Swift não tem antiguidade predefinida para exponenciação. Na álgebra, a exponenciação deve ser calculada antes da multiplicação / divisão. Assim, criamos uma ordem de execução personalizada, colocando a exponenciação maior que a multiplicação. - Este anúncio: -  - precedencegroup ExponentPrecedence { higherThan: MultiplicationPrecedence associativity: right } infix operator ^^: ExponentPrecedence
 - Esta é a implementação: -  - func ^^(base: Int, exponent: Int) -> Int { let l = Double(base) let r = Double(exponent) let p = pow(l, r) return Int(p) }
 
 
 - Pergunta 3- O código a seguir define a estrutura do  Pizza-  e o protocolo  Pizzeria-  com uma extensão para a implementação padrão do método  makeMargherita ()-  : -  - struct Pizza { let ingredients: [String] } protocol Pizzeria { func makePizza(_ ingredients: [String]) -> Pizza func makeMargherita() -> Pizza } extension Pizzeria { func makeMargherita() -> Pizza { return makePizza(["tomato", "mozzarella"]) } }
 - Agora definimos o restaurante  Lombardis-  : -  - struct Lombardis: Pizzeria { func makePizza(_ ingredients: [String]) -> Pizza { return Pizza(ingredients: ingredients) } func makeMargherita() -> Pizza { return makePizza(["tomato", "basil", "mozzarella"]) } }
 - O código a seguir cria duas instâncias do  Lombardis-  . Qual deles faz margarita com manjericão? -  - let lombardis1: Pizzeria = Lombardis() let lombardis2: Lombardis = Lombardis() lombardis1.makeMargherita() lombardis2.makeMargherita()
 - a resposta- Em ambos. O protocolo  Pizzeria-  declara o método  makeMargherita ()-  e fornece uma implementação padrão. A implementação do  Lombardis-  substitui o método padrão. Como declaramos o método no protocolo em dois lugares, a implementação correta será chamada. - Mas e se o protocolo não declarasse o método  makeMargherita ()-  e a extensão ainda fornecesse a implementação padrão, assim: -  - protocol Pizzeria { func makePizza(_ ingredients: [String]) -> Pizza } extension Pizzeria { func makeMargherita() -> Pizza { return makePizza(["tomato", "mozzarella"]) } }
 - Nesse caso, apenas lombardis2 teria pizza com manjericão, enquanto lombardis1 não teria pizza, porque usaria o método definido em extensão. 
 
 - Pergunta 4- O código a seguir não compila. Você pode explicar o que há de errado com ele? Sugira soluções para o problema. -  - struct Kitten { } func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") } print(k) }
 - Dica: existem três maneiras de corrigir o erro. - a resposta- O bloco  else-  da declaração de  guarda-  requer uma opção de saída, usando  return-  , lançando uma exceção ou chamando  @noreturn-  . A solução mais simples é adicionar uma  declaração de retorno-  . -  - func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") return } print(k) }
 - Esta solução lançará uma exceção: -  - enum KittenError: Error { case NoKitten } struct Kitten { } func showKitten(kitten: Kitten?) throws { guard let k = kitten else { print("There is no kitten") throw KittenError.NoKitten } print(k) } try showKitten(kitten: nil)
 - Finalmente, aqui está a chamada para  fatalError ()-  , a função  @noreturn-  . -  - struct Kitten { } func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") fatalError() } print(k) }
 
 
 
 
 - perguntas orais- Pergunta 1- Os fechamentos são um tipo de referência ou valor? - a resposta- Os fechamentos são um tipo de referência. Se você atribuir um fechamento a uma variável e copiá-lo para outra variável, copie o link para o mesmo fechamento e sua lista de capturas.
 
 
 
 - Questão 2- Você usa o tipo  UInt-  para armazenar um número inteiro não assinado. Ele implementa um inicializador para converter de um todo com um sinal: -  - init(_ value: Int)
 - No entanto, o código a seguir não será compilado se você especificar um valor negativo: -  - let myNegative = UInt(-1)
 - Inteiros assinados, por definição, não podem ser negativos. No entanto, é possível usar a representação de um número negativo na memória para convertê-lo em um número não assinado. - Como posso converter um número inteiro negativo para UInt, mantendo sua representação na memória? - a resposta- Há um inicializador para isso: -  - UInt(bitPattern: Int)
 - E use: -  - let myNegative = UInt(bitPattern: -1)
 
 
 - Pergunta 3- Descreva referências circulares no Swift? Como eles podem ser consertados? - a resposta- As referências circulares ocorrem quando duas instâncias contêm uma forte referência uma à outra, o que leva a um vazamento de memória devido ao fato de que nenhuma dessas instâncias pode ser liberada. Uma instância não pode ser liberada enquanto ainda houver fortes referências a ela, mas uma instância mantém a outra.
 
 Isso pode ser resolvido substituindo o link de um lado, especificando a palavra-chave fraca ou sem dono .
 
 
 
 - Pergunta 4- Swift permite criar enumerações recursivas. Aqui está um exemplo dessa enumeração que contém uma variante de Nó com dois tipos associativos, T e Lista: -  - enum List<T> { case node(T, List<T>) }
 - Haverá um erro de compilação. Do que perdemos? - a resposta- Esquecemos a palavra-chave  indireta-  , que permite opções de enumeração recursiva semelhantes: -  - enum List<T> { indirect case node(T, List<T>) }