language apple ios swift optional swift2

ios - apple - swift reference



¿Cuándo debo comparar un valor opcional a cero? (5)

Casi siempre es innecesario verificar si un opcional no es nil . Prácticamente, el único momento en que necesita hacer esto es si su nulidad es lo único que desea saber: no le importa lo que hay en el valor, solo que no es nil .

En la mayoría de las demás circunstancias, hay un poco de taquigrafía Swift que puede hacer la tarea de manera más segura y concisa dentro del if para usted.

Usar el valor si no es nil

En lugar de:

let s = "1" let i = Int(s) if i != nil { print(i! + 1) }

puede usar if let :

if let i = Int(s) { print(i + 1) }

También puedes usar var :

if var i = Int(s) { print(++i) // prints 2 }

pero tenga en cuenta que seré una copia local ; cualquier cambio en i no afectará el valor dentro del original opcional.

Puede desenvolver múltiples opciones dentro de una sola if let , y las posteriores pueden depender de las anteriores:

if let url = NSURL(string: urlString), data = NSData(contentsOfURL: url), image = UIImage(data: data) { let view = UIImageView(image: image) // etc. }

También puede agregar cláusulas where a los valores sin envolver:

if let url = NSURL(string: urlString) where url.pathExtension == "png", let data = NSData(contentsOfURL: url), image = UIImage(data: data) { etc. }

Reemplazar nil con un valor predeterminado

En lugar de:

let j: Int if i != nil { j = i } else { j = 0 }

o:

let j = i != nil ? i! : 0

puedes usar el operador de fusión nula, ?? :

// j will be the unwrapped value of i, // or 0 if i is nil let j = i ?? 0

Igualar un opcional con un no opcional

En lugar de:

if i != nil && i! == 2 { print("i is two and not nil") }

puede verificar si los opcionales son iguales a valores no opcionales:

if i == 2 { print("i is two and not nil") }

Esto también funciona con comparaciones:

if i < 5 { }

nil siempre es igual a otros nil s, y es menor que cualquier valor no nil .

¡Ten cuidado! Puede haber gotchas aquí:

let a: Any = "hello" let b: Any = "goodbye" if (a as? Double) == (b as? Double) { print("these will be equal because both nil...") }

Llamar a un método (o leer una propiedad) en una opción

En lugar de:

let j: Int if i != nil { j = i.successor() } else { // no reasonable action to take at this point fatalError("no idea what to do now...") }

puedes usar encadenamiento opcional, ?. :

let j = i?.successor()

Tenga en cuenta que ahora j también será opcional para dar cuenta del escenario fatalError . Más adelante, puede usar una de las otras técnicas en esta respuesta para manejar la opcionalidad de j , pero a menudo puede diferir el desenvolvimiento de sus opcionales hasta mucho más tarde, o, a veces, en absoluto.

Como su nombre lo indica, puede encadenarlos, para que pueda escribir:

let j = s.toInt()?.successor()?.successor()

El encadenamiento opcional también funciona con subíndices:

let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]] let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}

y funciones:

let dictOfFuncs: [String:(Int,Int)->Int] = [ "add":(+), "subtract":(-) ] dictOfFuncs["add"]?(1,1) // returns {Some 2}

Asignación a una propiedad en un opcional

En lugar de:

if splitViewController != nil { splitViewController!.delegate = self }

Puede asignar a través de una cadena opcional:

splitViewController?.delegate = self

Solo si splitViewController no es nil se realizará la asignación.

Usar el valor si no es nil o rescatar (nuevo en Swift 2.0)

A veces, en una función, hay un poco de código que desea escribir para verificar un opcional, y si es nil , salga de la función temprano, de lo contrario continúe.

Puedes escribir esto así:

func f(s: String) { let i = Int(s) if i == nil { fatalError("Input must be a number") } print(i! + 1) }

o para evitar la fuerza de desenvolver, así:

func f(s: String) { if let i = Int(s) { print(i! + 1) } else { fatalErrr("Input must be a number") } }

pero es mucho mejor mantener el código de manejo de errores en la parte superior junto al cheque. Esto también puede conducir a una anidación desagradable (la "pirámide del destino").

En cambio, puede usar guard , que es como if not let :

func f(s: String) { guard let i = Int(s) else { fatalError("Input must be a number") } // i will be an non-optional Int print(i+1) }

La parte else debe salir del alcance del valor protegido, por ejemplo, un return o fatalError , para garantizar que el valor protegido sea válido por el resto del alcance.

guard no se limita al alcance de la función. Por ejemplo lo siguiente:

var a = ["0","1","foo","2"] while !a.isEmpty { guard let i = Int(a.removeLast()) else { continue } print(i+1, appendNewline: false) }

impresiones 321 .

Bucle sobre elementos no nulos en una secuencia (nuevo en Swift 2.0)

Si tiene una secuencia de opciones, puede usar for case let _? iterar sobre todos los elementos no opcionales:

let a = ["0","1","foo","2"] for case let i? in a.map({ Int($0)}) { print(i+1, appendNewline: false) }

impresiones 321 . Esto está utilizando la sintaxis de coincidencia de patrones para un opcional, que es un nombre de variable seguido de ? .

También puede usar esta coincidencia de patrones en las declaraciones de switch :

func add(i: Int?, _ j: Int?) -> Int? { switch (i,j) { case (nil,nil), (_?,nil), (nil,_?): return nil case let (x?,y?): return x + y } } add(1,2) // 3 add(nil, 1) // nil

Looping hasta que una función devuelve nil

Al igual que if let , también puedes escribir while let y loop hasta nil :

while let line = readLine() { print(line) }

También puede escribir while var (advertencias similares a if var aplica if var ).

where cláusulas también funcionan aquí (y terminan el ciclo, en lugar de omitir):

while let line = readLine() where !line.isEmpty { print(line) }

Pasar un opcional a una función que toma un no opcional y devuelve un resultado

En lugar de:

let j: Int if i != nil { j = abs(i!) } else { // no reasonable action to take at this point fatalError("no idea what to do now...") }

puedes usar el operador de map opcional:

let j = i.map { abs($0) }

Esto es muy similar al encadenamiento opcional, pero para cuando necesita pasar el valor no opcional a la función como argumento. Al igual que con el encadenamiento opcional, el resultado será opcional.

Esto es bueno cuando quieres un opcional de todos modos. Por ejemplo, reduce1 es como reduce , pero usa el primer valor como semilla, devolviendo un opcional en caso de que la matriz esté vacía. Puede escribirlo de esta manera (usando la palabra clave guard de antes):

extension Array { func reduce1(combine: (T,T)->T)->T? { guard let head = self.first else { return nil } return dropFirst(self).reduce(head, combine: combine) } } [1,2,3].reduce1(+) // returns 6

Pero en su lugar, podría map la propiedad .first y devolver eso:

extension Array { func reduce1(combine: (T,T)->T)->T? { return self.first.map { dropFirst(self).reduce($0, combine: combine) } } }

Pasar un opcional a una función que toma un opcional y devuelve un resultado, evitando molestos dobles opcionales

A veces, quieres algo similar al map , pero la función que quieres llamar se devuelve opcional. Por ejemplo:

// an array of arrays let arr = [[1,2,3],[4,5,6]] // .first returns an optional of the first element of the array // (optional because the array could be empty, in which case it''s nil) let fst = arr.first // fst is now [Int]?, an optional array of ints // now, if we want to find the index of the value 2, we could use map and find let idx = fst.map { find($0, 2) }

Pero ahora idx es de tipo Int?? , un doble opcional. En su lugar, puede usar flatMap , que "aplana" el resultado en un solo opcional:

let idx = fst.flatMap { find($0, 2) } // idx will be of type Int? // and not Int?? unlike if `map` was used

Muy a menudo, necesita escribir código como el siguiente:

if someOptional != nil { // do something with the unwrapped someOptional e.g. someFunction(someOptional!) }

Esto parece un poco detallado, y también escuché que usando el ! El operador de desenrollado forzado puede ser inseguro y mejor evitarlo. ¿Hay una mejor manera de manejar esto?


Creo que deberías volver al libro de programación Swift y aprender para qué sirven estas cosas. ! se usa cuando está absolutamente seguro de que lo opcional no es nulo. Dado que declaró que está absolutamente seguro, se bloquea si se equivoca. Lo cual es completamente intencional. Es "inseguro y mejor evitarlo" en el sentido de que las afirmaciones en su código son "inseguras y mejor evitarlas". Por ejemplo:

if someOptional != nil { someFunction(someOptional!) }

Los ! Es absolutamente seguro. A menos que haya un gran error en su código, como escribir por error (espero que detecte el error)

if someOptional != nil { someFunction(SomeOptional!) }

en cuyo caso su aplicación puede bloquearse, usted investiga por qué se bloquea y repara el error, que es exactamente para lo que está allí el bloqueo. Un objetivo en Swift es que, obviamente, su aplicación debería funcionar correctamente, pero dado que Swift no puede hacer cumplir esto, hace que su aplicación funcione correctamente o se bloquee si es posible, por lo que los errores se eliminan antes de que se envíe la aplicación.


Después de mucho pensar e investigar, se me ocurrió la forma más fácil de desenvolver un opcional:

  • Cree un nuevo archivo Swift y asígnele el nombre UnwrapOperator.swift

  • Pegue el siguiente código en el archivo:

    import Foundation import UIKit protocol OptionalType { init() } extension String: OptionalType {} extension Int: OptionalType {} extension Int64: OptionalType {} extension Float: OptionalType {} extension Double: OptionalType {} extension CGFloat: OptionalType {} extension Bool: OptionalType {} extension UIImage : OptionalType {} extension IndexPath : OptionalType {} extension NSNumber : OptionalType {} extension Date : OptionalType {} extension UIViewController : OptionalType {} postfix operator *? postfix func *?<T: OptionalType>( lhs: T?) -> T { guard let validLhs = lhs else { return T() } return validLhs } prefix operator / prefix func /<T: OptionalType>( rhs: T?) -> T { guard let validRhs = rhs else { return T() } return validRhs }

  • Ahora el código anterior ha creado 2 operadores [Un prefijo y un postfix].

  • En el momento de desenvolver puede usar cualquiera de estos operadores antes o después de las opciones
  • La explicación es simple, los operadores devuelven el valor del constructor si obtienen cero en la variable, de lo contrario, el valor contenido dentro de la variable.

  • A continuación se muestra el ejemplo de uso:

    var a_optional : String? = "abc" var b_optional : Int? = 123 // before the usage of Operators print(a_optional) --> Optional("abc") print(b_optional) --> Optional(123) // Prefix Operator Usage print(/a_optional) --> "abc" print(/b_optional) --> 123 // Postfix Operator Usage print(a_optional*?) --> "abc" print(b_optional*?) --> 123

  • A continuación se muestra el ejemplo cuando la variable contiene nil :

    var a_optional : String? = nil var b_optional : Int? = nil // before the usage of Operators print(a_optional) --> nil print(b_optional) --> nil // Prefix Operator Usage print(/a_optional) --> "" print(/b_optional) --> 0 // Postfix Operator Usage print(a_optional*?) --> "" print(b_optional*?) --> 0

  • Ahora puede elegir qué operador utiliza, ambos sirven para el mismo propósito.


Podemos usar encuadernación opcional.

var x:Int? if let y = x { // x was not nil, and its value is now stored in y } else { // x was nil }


Tú hay un camino. Se llama encadenamiento opcional . De la documentación:

El encadenamiento opcional es un proceso para consultar y llamar a propiedades, métodos y subíndices en un opcional que actualmente puede ser nulo. Si el opcional contiene un valor, la propiedad, el método o la llamada del subíndice tienen éxito; si el opcional es nil, la propiedad, el método o la llamada de subíndice devuelve nil. Se pueden encadenar varias consultas juntas, y la cadena completa falla correctamente si algún enlace de la cadena es nulo.

Aquí hay un ejemplo

class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } let john = Person() if let roomCount = john.residence?.numberOfRooms { println("John''s residence has /(roomCount) room(s).") } else { println("Unable to retrieve the number of rooms.") } // prints "Unable to retrieve the number of rooms."

Puedes consultar el artículo completo aquí .