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í .