Preguntas y respuestas rápidas de la entrevista

Nota de actualización : Bill Morefield actualizó este artículo para XCode 10.2 y Swift 5. Antonio Bello escribió el original.

Swift tiene solo cuatro años, pero ya se ha convertido en el idioma predeterminado para el desarrollo de iOS. Como Swift ha evolucionado a la versión 5.0, se ha convertido en un lenguaje complejo y poderoso que abarca paradigmas orientados a objetos y funcionales. Cada lanzamiento trae más evolución y cambio.

¿Pero qué tan bien conoces a Swift? En este artículo, encontrará algunas preguntas de la entrevista de Swift.

Puede usar estas preguntas para entrevistar a los candidatos para probar su conocimiento de Swift. ¡O puedes probar el tuyo! Si no sabe una respuesta, no se preocupe: cada pregunta tiene una solución para que pueda aprender.

Encontrará las preguntas separadas en tres niveles:

  • Principiante : Adecuado para un principiante a Swift. Ha leído un libro o dos sobre el tema y ha trabajado con Swift en sus propias aplicaciones.
  • Intermedio : Adecuado para alguien con un gran interés en el idioma. Has estado leyendo mucho acerca de Swift y experimentando más con él.
  • Avanzado : adecuado para los desarrolladores más experimentados, la gente que disfruta explorando el lenguaje a fondo y utilizando técnicas de vanguardia.

En cada nivel, encontrará dos tipos de preguntas:

  • Preguntas escritas : buenas para las pruebas de programación de correo electrónico, ya que estas pueden incluir la escritura de código.
  • Preguntas verbales : Es bueno preguntar por teléfono o en una entrevista cara a cara, ya que su posible cliente puede responderlas verbalmente.

Mientras analiza estas preguntas y respuestas, mantenga un área de juegos abierta para que pueda jugar con el código adjunto. a la pregunta antes de contestar. Hemos probado todas las respuestas con Xcode 10.2 y Swift 5.

Preguntas escritas para principiantes

Pregunta # 1

Considere lo siguiente:

 struct Tutorial {
  dificultad var: Int = 1
}

var tutorial1 = Tutorial ()
var tutorial2 = tutorial1
tutorial2.difficulty = 2

¿Cuáles son los valores de tutorial1.difficulty y tutorial2.difficulty ? ¿Sería esto diferente si Tutorial fuera una clase? ¿Por qué o por qué no?

[spoiler title=”Answer”]

tutorial1.difficulty is 1 mientras que tutorial2.difficulty 2 2 2

Las estructuras en Swift son tipos de valor. Copia los tipos de valor por valor en lugar de por referencia. El siguiente código crea una copia de tutorial1 y la asigna a tutorial2 :

 var tutorial2 = tutorial1

Un cambio a tutorial2 es no reflejado en tutorial1 .

If Tutorial fue una clase, ambas

tutorial1.difficulty y tutorial2.difficulty sería 2 . Las clases en Swift son tipos de referencia. Cuando cambies una propiedad de tutorial1 la verás reflejada en tutorial2 y viceversa.

[/spoiler]

Pregunta # 2

Has declarado view1 con var y ha declarado view2 con let . ¿Cuál es la diferencia? ¿Se compilará la última línea?

 importará UIKit

var view1 = UIView ()
view1.alpha = 0.5

deja view2 = UIView ()
view2.alpha = 0.5 // ¿Se compilará esta línea?

[spoiler title=”Answer”]

Sí, la última línea se compilará. view1 es una variable, y puede reasignarla a una nueva instancia de UIView . Con permita puede asignar un valor solo una vez, por lo que el siguiente código no se compilaría:

 view2 = view1 // Error: view2 es inmutable

Sin embargo, UIView es una clase con semántica de referencia, por lo que puedes mutar las propiedades de view2 – lo que significa que la última línea se compilará:

 let view2 = UIView ()
view2.alpha = 0.5 // ¡Sí!

[/spoiler]

Pregunta # 3

Este código complicado ordena una matriz de nombres alfabéticamente. Simplifique el cierre tanto como pueda.

 var animals = ["fish", "cat", "chicken", "dog"]
animals.sort {(one: String, two: String) -> Bool in
    devuelve uno <dos
}
imprimir (animales)

[spoiler title=”Answer”]

El sistema de inferencia de tipos calcula automáticamente el tipo de los parámetros en el cierre y el tipo de retorno, por lo que puede deshacerse de ellos:

 animals.sort {(uno, dos) en devuelve uno <dos}

Puede sustituir la notación $ i por los nombres de los parámetros:

 animals.sort {return $ 0 <$ 1}

En los cierres de una sola declaración, puede omitir la palabra clave return . El valor de la última declaración se convierte en el valor de retorno del cierre:

 animals.sort {$ 0 <$ 1}

Finalmente, ya que Swift sabe que los elementos de la matriz se ajustan a Equatable puedes simplemente escribir:

 animals.sort (por: <)

[/spoiler]

Pregunta # 4

Este código crea dos clases: Dirección y Persona . Luego crea dos instancias de Person para representar a Ray y Brian.

 class Address {
  var fullAddress: String
  var city: String
  
  init (fullAddress: String, city: String) {
    self.fullAddress = fullAddress
    Ciudad propia = ciudad
  }
}

clase persona {
  nombre var: cadena
  Dirección var: Dirección
  
  init (nombre: Cadena, dirección: Dirección) {
    self.name = name
    autodirección = dirección
  }
}

varquarters = Dirección (dirección completa: "123 Tutorial Street", ciudad: "Appletown")
var ray = Persona (nombre: "Ray", dirección: sede)
var brian = Persona (nombre: "Brian", dirección: sede)

Supongamos que Brian se muda al nuevo edificio al otro lado de la calle; querrás actualizar su registro de esta manera:

 brian.address.fullAddress = "148 Tutorial Street"

Esto se compila y ejecuta sin error. Si comprueba la dirección de Ray ahora, también se mudó al nuevo edificio.

 print (ray.address.fullAddress)

¿Qué está pasando aquí? ¿Cómo se puede solucionar el problema?

[spoiler title=”Answer”]

La dirección es una clase y tiene una semántica de referencia así que la sede es la misma instancia, ya sea que acceda a ella a través de rayo o brian . Cambiar la dirección de sede la cambiará para ambos. ¿Te imaginas lo que pasaría si Brian recibiera el correo de Ray o viceversa? :]

La solución es crear una nueva Dirección para asignarla a Brian, o declarar Dirección como una estructura en lugar de una clase.

Eres bueno, pero aún no puedes reclamar el estatus de Jedi.

Para responder a algunas de estas preguntas, es posible que deba jugar con el código en un patio de recreo.

Preguntas verbales para principiantes

Pregunta # 1 [19659015] ¿Qué es una opción y qué problema resuelven las opciones?

[spoiler title=”Answer”]

Una opción opcional permite que una variable de cualquier tipo represente una falta de valor . En Objective-C, la ausencia de valor está disponible solo en los tipos de referencia que utilizan el valor especial nil . Los tipos de valor, como int o float no tienen esta capacidad.

Swift extiende el concepto de falta de valor a los tipos de referencia y valor con opciones . Una variable opcional puede contener un valor o nil que indica una falta de valor.

[/spoiler]

Pregunta # 2

Resume las principales diferencias entre una estructura y una clase .

[spoiler title=”Answer”]

Puede resumir las diferencias de la siguiente manera:

  • Las clases admiten la herencia; las estructuras no.
  • Las clases son tipos de referencia; las estructuras son tipos de valor.

[/spoiler]

Pregunta # 3

¿Qué son los genéricos y qué problema resuelven?

[spoiler title=”Answer”]

En Swift, puedes usar genéricos tanto en funciones como en tipos de datos, por ejemplo. en clases, estructuras o enumeraciones.

Los genéricos resuelven el problema de la duplicación de código. Cuando tiene un método que toma un tipo de parámetro, es común duplicarlo para acomodar un parámetro de un tipo diferente.

Por ejemplo, en el siguiente código, la segunda función es un "clon" de la primera, excepto que acepta cadenas en lugar de números enteros.

 func areIntEqual (_ x: Int, _ y: Int) -> Bool {
  devuelve x == y
}

func areStringsEqual (_ x: String, _ y: String) -> Bool {
  devuelve x == y
}

areStringsEqual ("ray", "ray") // true
areIntEqual (1, 1) // true

Al adoptar los genéricos, puedes combinar las dos funciones en una y mantener la seguridad de tipo al mismo tiempo. Aquí está la implementación genérica:

 func areTheyEqual  (_ x: T, _ y: T) -> Bool {
  devuelve x == y
}

areTheyEqual ("ray", "ray")
areTheyEqual (1, 1)

Ya que está probando la igualdad en este caso, restringe los parámetros a cualquier tipo que implemente el protocolo Equatable . Este código logra el resultado deseado y evita que se pasen parámetros de un tipo diferente.

[/spoiler]

Pregunta # 4

En algunos casos, no puede evitar el uso de opciones no envueltas implícitamente. ¿Cuando? ¿Por qué?

[spoiler title=”Answer”]

Las razones más comunes para usar opciones opcionales sin desenvolver son:

  1. Cuando no puede inicializar una propiedad que no sea nil por naturaleza en el momento de la creación de instancias. Un ejemplo típico es una salida de Interface Builder, que siempre se inicializa después de su propietario. En este caso específico, suponiendo que esté configurado correctamente en Interface Builder, ha garantizado que la salida no es nula antes de usarla.
  2. Para resolver el problema del ciclo de referencia fuerte, que es cuando dos instancias se refieren entre sí y requiere una referencia no nula a la otra instancia. En tal caso, marca un lado de la referencia como sin dueño mientras que el otro usa un opcional implícitamente sin envolver.

[/spoiler]

Pregunta # 5

¿Cuáles son las diversas formas de desenvolver un opcional? ¿Cómo califican en términos de seguridad?

 var x: String? = "Prueba"

Sugerencia: hay siete maneras.

[spoiler title=”Answer”]

Desenvolvimiento forzado – inseguro.

 deja a: Cadena = x!

Declaración de variable de desenvuelta implícitamente, insegura en muchos casos.

 var a = x!

Encuadernación opcional – segura.

 si se deja a = x {
  print ("x se desenvolvió correctamente y es =  (a)")
}

Encadenamiento opcional – seguro.

 deja a = x? .Count

Operador de coalescencia nulo – seguro.

 deja a = x? ""

Declaración del guardia – seguro.

 el guardia deja a = x else {
  regreso
}

Patrón opcional – seguro.

 en caso de que sea un? = x {
  imprimir (a)
}

[/spoiler]

Preguntas escritas intermedias

Ahora, para aumentar un poco la dificultad. ¿Estás listo?

Pregunta # 1

¿Cuál es la diferencia entre nil y .none ?

[spoiler title=”Answer”]

No hay diferencia, como Opcional. ninguno ( .none para abreviar) y nil son ​​equivalentes.

De hecho, esta declaración arroja verdadero:

 nil == .none

El uso de nil es más común y es la convención recomendada.

[/spoiler]

Pregunta # 2

Aquí hay un modelo de termómetro como clase y estructura. El compilador se quejará de la última línea. ¿Por qué no se compila?

Sugerencia : Lea el código con cuidado y piénselo antes de probarlo en un patio de recreo.

 clase pública ThermometerClass {
  Temperatura var (privada): doble = 0.0
  función pública registerTemperature (_ temperatura: doble) {
    temperatura de sí mismo = temperatura
  }
}

let thermometerClass = ThermometerClass ()
thermometerClass.registerTemperature (56.0)

Estructura de termómetro público {
  Temperatura var (privada): doble = 0.0
  función de mutación pública registroTemperatura (_ temperatura: doble) {
    temperatura de sí mismo = temperatura
  }
}

dejar thermometerStruct = ThermometerStruct ()
thermometerStruct.registerTemperature (56.0)

[spoiler title=”Answer”]

El ThermometerStruct se declara correctamente con una función de mutación para cambiar su variable interna temperatura . El compilador se queja porque ha invocado registerTemperature en una instancia creada a través de let que por lo tanto es inmutable. Cambie deje a var para que el ejemplo se compile.

Con estructuras, debe marcar los métodos que cambian el estado interno como mutando pero no puede invocarlos de variables inmutables.

[/spoiler]

Pregunta # 3

¿Qué se imprimirá este código y por qué?

 var thing = "cars"

dejar cierre = {[thing] en
  imprimir ("I love  (thing)")
}

cosa = "aviones"

cierre()

[spoiler title=”Answer”]

Se imprimirá: Me encantan los autos . La lista de captura crea una copia de cosa cuando declara el cierre. Esto significa que el valor capturado no cambia incluso si asigna un nuevo valor a cosa .

Si omite la lista de captura en el cierre, entonces el compilador usa una referencia en lugar de una copia. Por lo tanto, cuando invoca el cierre, refleja cualquier cambio en la variable. Puedes ver esto en el siguiente código:

 var thing = "cars"

dejar cierre = {
  imprimir ("I love  (thing)")
}

cosa = "aviones"

cierre () // Estampas: "Me encantan los aviones"

[/spoiler]

Pregunta # 4

Aquí hay una función global que cuenta el número de valores únicos en una matriz:

 func countUniques  (_ array: Array ) -> Int {
  dejar ordenado = array.sorted ()
  dejar inicial: (T ?, Int) = (.none, 0)
  dejar reducido = ordenado.reducir (inicial) {
    ($ 1, $ 0.0 == $ 1? $ 0.1: $ 0.1 + 1)
  }
  retorno reducido.1
}

Utiliza ordenado por lo que restringe T a los tipos que se ajustan a Comparable .

Lo llamas así:

 countUniques ([1, 2, 3, 3]) // el resultado es 3

Reescriba esta función como un método de extensión en Array para que pueda escribir algo como esto:

 [1, 2, 3, 3] .countUniques () // debe imprimir 3

[spoiler title=”Answer”]

Puedes reescribir la extensión global countUniques (_:) como una extensión Array :

 extensión Array donde Element: Comparable {
  func countUniques () -> Int {
    dejar ordenados valores = ordenados ()
    dejar inicial: (Elemento, Int) = (.none, 0)
    let reducido = ordenadoValores.reduce (inicial) {
      ($ 1, $ 0.0 == $ 1? $ 0.1: $ 0.1 + 1)
    }
    retorno reducido.1
  }
}

Tenga en cuenta que el nuevo método solo está disponible cuando el tipo genérico Elemento se ajusta a Comparable .

[/spoiler]

Pregunta # 5

Aquí hay una función Para dividir dos dobles opcionales. Hay tres condiciones previas para verificar antes de realizar la división real:

  • El dividendo debe contener un valor no nil .
  • El divisor debe contener un valor no nil . [19659062] El divisor no debe ser cero.
 func divide (_ dividendo: ¿Doble ?, ¿por divisor: ¿Doble?) -> ¿Doble? {
  si dividendo == nil {
    volver a cero
  }
  si divisor == nil {
    volver a cero
  }
  si divisor == 0 {
    volver a cero
  }
  devolver dividendo! / divisor!
}

Mejore esta función utilizando la declaración guard y y sin usar el desenvolvimiento forzado.

[spoiler title=”Answer”]

La declaración de la guardia introdujo en Swift 2.0 proporciona una ruta de salida cuando no se cumple una condición. Es muy útil para verificar las condiciones previas porque le permite expresarlas de manera clara, sin la pirámide de la fatalidad de las declaraciones anidadas if . Aquí hay un ejemplo:

 guard dividendo! = Nil else {return nil}

También puede usar la instrucción guard para el enlace opcional, que hace que la variable desenvuelta sea accesible después de la declaración guard :

 guard let dividend = dividend else {return .none }

Entonces, puede reescribir la función divide como:

 func divide (_ dividendo: ¿Doble ?, ¿por divisor: ¿Doble?) -> ¿Doble? {
  guardia deja dividendo = dividendo else {return nil}
  guard let divisor = divisor else {return nil}
  guard divisor! = 0 else {return nil}
  dividendo / divisor de retorno
}

Observe la ausencia de los operadores implícitamente desenvueltos en la última línea porque desenvolvió tanto el dividendo como divisor y los almacenó en variables inmutables no opcionales.

Nota que los resultados de los opcionales no envueltos en una declaración guard están disponibles para el resto del bloque de código en el que aparece la declaración.

Puede simplificar esto aún más agrupando la protección declaraciones:

 func divide (_ dividendo: ¿Doble ?, ¿por divisor: ¿Doble?) -> ¿Doble? {
  Guardia
    dejar dividendo = dividendo,
    vamos divisor = divisor,
    divisor! = 0
    else {
      volver a cero
    }
  dividendo / divisor de retorno
}

[/spoiler]

Pregunta # 6

Reescriba el método de la pregunta cinco usando una if

[spoiler title=”Answer”]

The ] declaración le permite desenvolver opciones y usar el valor dentro de ese bloque de código. Tenga en cuenta que no puede acceder al desempaquetado opcional fuera del bloque. Puede escribir la función usando si se permite la instrucción como:

 división de función (_ dividendo: ¿Doble ?, ¿por divisor: ¿Doble?) -> ¿Doble? {
  Si
    dejar dividendo = dividendo,
    vamos divisor = divisor,
    divisor! = 0 {
      dividendo / divisor de retorno
  } else {
    volver a cero
  }
}

[/spoiler]

Preguntas verbales intermedias

Pregunta # 1

En Objective-C, declara una constante como esta:

 const int number = 0;

Aquí está la contraparte de Swift:

 let number = 0

¿Cuáles son las diferencias entre ellos?

[spoiler title="Answer"]

A const es una variable que se inicializa en tiempo de compilación con un valor o una expresión que debe resolverse en tiempo de compilación. [19659002] Un inmutable creado con let es una constante determinada en el tiempo de ejecución. Puedes inicializarlo con una expresión estática o dinámica. Esto permite una declaración como:

 let higherNumber = number + 5

Tenga en cuenta que solo puede asignar su valor una vez.

[/spoiler]

Pregunta # 2

Para declarar una propiedad o función estática, use el modificador estático en los tipos de valor. Aquí hay un ejemplo para una estructura:

 struct Sun {
  función estática illuminate () {}
}

Para las clases, es posible usar el modificador static static o class . Logran el mismo objetivo, pero de diferentes maneras. ¿Puede explicar en qué se diferencian?

[spoiler title="Answer"]

static hace que una propiedad o una función static y no sea reemplazable . El uso de clase le permite anular la propiedad o función.

Cuando se aplica a las clases, estática se convierte en un alias para clase final .

Por ejemplo, en este código, el compilador se quejará cuando intente anular illuminate () :

 class Star {
  clase func spin () {}
  función estática illuminate () {}
}
clase sol: estrella {
  sobrescribir la función func spin () {
    super.spin ()
  }
  // error: el método de clase anula un método de clase 'final'
  anular la función estática illuminate () {
    super.iluminar ()
  }
}

[/spoiler]

Pregunta # 3

¿Puede agregar una propiedad almacenada a un tipo usando una extensión? ¿Cómo o por qué no?

[spoiler title="Answer"]

No, no es posible. Puede usar una extensión para agregar un nuevo comportamiento a un tipo existente, pero no para alterar ni el tipo en sí ni su interfaz. Si agrega una propiedad almacenada, necesitará memoria adicional para almacenar el nuevo valor. Una extensión no puede administrar dicha tarea.

[/spoiler]

Pregunta # 4

¿Qué es un protocolo en Swift?

[spoiler title="Answer"]

Un protocolo es un tipo que define un plan de métodos , propiedades y otros requisitos. Una clase, estructura o enumeración puede adoptar el protocolo para implementar esos requisitos.

Un tipo que adopta los requisitos de un protocolo cumple con ese protocolo. El protocolo no implementa ninguna funcionalidad en sí, sino que define la funcionalidad. Puede extender un protocolo para proporcionar una implementación predeterminada de algunos de los requisitos o la funcionalidad adicional que pueden aprovechar los tipos conformes.

[/spoiler]

Preguntas escritas avanzadas

Pregunta # 1

Considere la siguiente estructura que modela un termómetro:

 Public struct Thermometer {
  temperatura de var público: doble
  inicio público (temperatura: doble) {
    temperatura de sí mismo = temperatura
  }
}

Para crear una instancia, puede usar este código:

 var t: Termómetro = Termómetro (temperatura: 56.8)

Pero sería mejor iniciarlo de esta manera:

 termómetro var: Termómetro = 56.8

¿Puedes? ¿Cómo?

[spoiler title="Answer"]

Swift define protocolos que le permiten inicializar un tipo con valores literales utilizando el operador de asignación. Adoptar el protocolo correspondiente y proporcionar un inicializador público permite la inicialización literal de un tipo específico. En el caso de Termómetro implementa ExpressibleByFloatLiteral de la siguiente manera:

 extensión Termómetro: ExpressibleByFloatLiteral {
  public init (floatLiteral value: FloatLiteralType) {
    self.init (temperatura: valor)
  }
}

Ahora, puede crear una instancia usando un flotador.

 var thermometer: Thermometer = 56.8

[/spoiler]

Pregunta # 2

Swift tiene un conjunto de operadores predefinidos para realizar operaciones aritméticas o lógicas. También permite la creación de operadores personalizados, ya sea unarios o binarios.

Defina e implemente un operador de energía personalizado ^^ con las siguientes especificaciones:

  • Toma dos Int s como parámetros.
  • Devuelve el primer parámetro elevado a la potencia del segundo.
  • Evalúa correctamente la ecuación usando el orden de operaciones algebraico estándar.
  • Ignora el potencial de errores de desbordamiento.

[spoiler title="Answer"] [19659002] Crea un nuevo operador personalizado en dos pasos: Declaración e implementación.

La declaración utiliza la palabra clave operator para especificar el tipo (unario o binario), la secuencia de caracteres que compone el operador, su asociatividad y prioridad. Swift 3.0 cambió la implementación de la prioridad para usar un grupo de precedencia.

Aquí, el operador es ^^ y el tipo es infix (binario). La asociatividad es derecha ; en otras palabras, los operadores de igual precedencia ^^ deben evaluar la ecuación de derecha a izquierda.

No hay precedencia estándar predefinida para operaciones exponenciales en Swift. En el orden estándar de operaciones para el álgebra, los exponentes deben calcularse antes de la multiplicación / división. Por lo tanto, deberá crear una precedencia personalizada que los coloque por encima de la multiplicación.

Aquí está la declaración:

 precedencegroup ExponentPrecedence {
  mayor Que: MultiplicacionPredecencia
  asociatividad: derecha
}
operador de infijo ^^: ExponentPrecedence

La ​​implementación sigue:

 func ^^ (base: Int, exponent: Int) -> Int {
  sea ​​l = doble (base)
  sea ​​r = doble (exponente)
  vamos p = pow (l, r)
  volver Int (p)
}

Tenga en cuenta que dado que el código no tiene en cuenta los desbordamientos, si la operación produce un resultado que Int no puede representar, como un valor mayor que Int.max entonces se produce un error de tiempo de ejecución.

[/spoiler]

Pregunta # 3

Considere el siguiente código que define Pizza como una estructura y Pizzeria como un protocolo con una extensión que incluye una implementación predeterminada para makeMargherita () :

 struct Pizza {
  dejar ingredientes: [String]
}

protocolo Pizzeria
  func makePizza (_ ingredientes: [String]) -> Pizza
  func makeMargherita () -> Pizza
}

extensión Pizzeria {
  func makeMargherita () -> Pizza {
    devolver makePizza (["tomato", "mozzarella"])
  }
}

Ahora definirá el restaurante Lombardi de la siguiente manera:

 struct Lombardis: Pizzeria {
  func makePizza (_ ingredientes: [String]) -> Pizza {
    volver Pizza (ingredientes: ingredientes)
  }

  func makeMargherita () -> Pizza {
    volver makePizza (["tomato", "basil", "mozzarella"])
  }
}

El siguiente código crea dos instancias de Lombardi's. ¿Cuál de los dos hará una margherita con albahaca?

 deja lombardis1: Pizzeria = Lombardis ()
vamos lombardis2: Lombardis = Lombardis ()

lombardis1.makeMargherita ()
lombardis2.makeMargherita ()

[spoiler title="Answer"]

Ambos lo hacen. El protocolo Pizzeria declara el método makeMargherita () y proporciona una implementación predeterminada. La implementación Lombardis anula el método predeterminado. Ya que declara el método en el protocolo en ambos casos, invocará la implementación correcta en el tiempo de ejecución.

¿Qué pasa si el protocolo no declara el método makeMargherita () pero la extensión aún proporciona una implementación por defecto, como esta?

 protocolo Pizzeria {
  func makePizza (_ ingredientes: [String]) -> Pizza
}

extensión Pizzeria {
  func makeMargherita () -> Pizza {
    devolver makePizza (["tomato", "mozzarella"])
  }
}

Aquí, solo lombardis2 haría la pizza con albahaca, mientras que lombardis1 haría una pizza sin ella, porque usaría el método definido en la extensión.

[/spoiler]

Pregunta # 4

El siguiente código tiene un error de tiempo de compilación. ¿Puedes verlo y explicar por qué sucede? ¿Cuáles son algunas maneras en que podría arreglarlo?

 struct Kitten {
}

func showKitten (kitten: Kitten?) {
  guardia deja k = gatito más {
    imprimir ("No hay gatito")
  }
  imprimir (k)
}

Sugerencia: Hay tres formas de corregir el error.

[spoiler title="Answer"]

El bloque else de una guardia requiere una ruta de salida, ya sea utilizando regresa lanzando una excepción o llamando a @noreturn . La solución más sencilla es agregar una declaración de devolución.

 func showKitten (kitten: Kitten?) {
  guardia deja k = gatito más {
    imprimir ("No hay gatito")
    regreso
  }
  imprimir (k)
}

Aquí hay una versión que lanza una excepción.

 enum KittenError: Error {
  caso NoKitten
}

estructura gatito {
}

func showKitten (gatito: ¿Gatito?) lanza {
  guardia deja k = gatito más {
    imprimir ("No hay gatito")
    lanzar KittenError.NoKitten
  }
  imprimir (k)
}

prueba showKitten (gatito: nil)

Finalmente, aquí hay una implementación que llama fatalError () que es una función @noreturn .

 struct Kitten {
}

func showKitten (kitten: Kitten?) {
  guardia deja k = gatito más {
    imprimir ("No hay gatito")
    error fatal()
  }
  imprimir (k)
}

[/spoiler]

Preguntas verbales avanzadas

Pregunta # 1

¿Son los valores de cierre o los tipos de referencia?

[spoiler title="Answer"]

Los cierres son tipos de referencia. Si asigna un cierre a una variable y copia la variable a otra variable, también copia una referencia al mismo cierre y su lista de captura.

[/spoiler]

Pregunta # 2

Usted use el tipo UInt para almacenar enteros sin signo. Implementa el siguiente inicializador para convertir desde un entero con signo:

 init (_ value: Int)

Sin embargo, el siguiente código genera una excepción de error de tiempo de compilación si proporciona un valor negativo:

 permite que myNegative = UInt (-1)

Un entero sin signo por definición no puede ser negativo. Sin embargo, es posible utilizar la representación en memoria de un número negativo para traducir a un entero sin signo. ¿Cómo se puede convertir un número negativo Int en un UInt mientras se mantiene su representación en la memoria?

[spoiler title="Answer"]

Hay un inicializador para eso:

 UInt (bitPattern : En t)

haciendo la implementación:

 deja myNegative = UInt (bitPattern: -1)

[/spoiler]

Pregunta # 3

¿Puede describir una referencia circular en Swift? ¿Cómo puede resolverlo?

[spoiler title="Answer"]

Una referencia circular ocurre cuando dos instancias mantienen una fuerte referencia entre sí, lo que provoca una pérdida de memoria porque ninguna de las dos instancias será desasignada. La razón es que no puede desasignar una instancia siempre que haya una referencia fuerte a ella, pero cada instancia mantiene viva a la otra debido a su fuerte referencia.

Resolvería el problema al romper la referencia circular fuerte al reemplazar una de las referencias fuertes por una débil o referencia .

Pregunta # 4

Swift permite la creación de enumeraciones recursivas. Aquí hay un ejemplo de tal enumeración con un caso Node que toma dos tipos de valores asociados, T y List :

 enum List  {
  nodo de caso (T, Lista )
}

Esto devuelve un error de compilación. ¿Cuál es la palabra clave que falta?

[spoiler title="Answer"]

Es la palabra clave indirecta que permite casos de enumeración recursivos como este:

 enum List  {
  nodo de caso indirecto (T, Lista )
}

[/spoiler]

¿Adónde ir desde aquí?

¡Felicitaciones por llegar al final de estas preguntas y respuestas, y no te sientas mal si no conocías todas las respuestas!

Algunas de estas preguntas son bastante complicadas. Swift es un lenguaje rico y expresivo y hay mucho que aprender sobre él. Apple sigue mejorando y cambiando, por lo que incluso los desarrolladores más experimentados no lo saben todo.

Para conocer Swift o aprovechar lo que ya sabes, asegúrate de consultar Swift Apprentice .

El recurso definitivo para todos los aspectos de Swift es la documentación oficial, Swift Programming Languageby Apple.

At the end of the day, using a language is the best way to learn it. Play with Swift in a playground or adopt it in a real project. Swift works (almost) seamlessly with Objective-C, so adding it to an existing project you already know is an excellent way to learn its ins and outs.

Thanks for visiting and working through these questions! Feel free to chime in below with your questions, problems and discoveries. I wouldn't mind if you wanted to pose some of your own challenges, too. See you in the forums!


READ MORE - CLICK HERE

www.Down.co.ve


from Down.co.ve http://bit.ly/2W45gok
via www.Down.co.ve

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s