Imprimir variable opcional

118

Estoy intentando con estas líneas de código

class Student {
    var name: String
    var age: Int?

    init(name: String) {
        self.name = name
    }

    func description() -> String {
        return age != nil ? "\(name) is \(age) years old." : "\(name) hides his age."
    }
}

var me = Student(name: "Daniel")
println(me.description())
me.age = 18
println(me.description())

El código anterior produce lo siguiente

Daniel hides his age.
Daniel is Optional(18) years old.

Mi pregunta es por qué hay Opcional (18) allí, ¿cómo puedo eliminar el opcional y solo imprimir

Daniel is 18 years old.
Daniel Adinugroho
fuente

Respuestas:

190

Tienes que entender qué es realmente un opcional. Muchos principiantes de Swift piensan var age: Int?que significa que la edad es un Int que puede tener o no un valor. Pero significa que la edad es un Opcional que puede o no tener un Int.

Dentro de su description()función no imprime el Int, sino que imprime el Optional. Si desea imprimir el Int, debe desenvolver el Opcional. Puede utilizar "enlace opcional" para desenvolver un opcional:

if let a = age {
 // a is an Int
}

Si está seguro de que el Opcional contiene un objeto, puede usar "desenvolvimiento forzado":

let a = age!

O en su ejemplo, dado que ya tiene una prueba para nil en la función de descripción, puede simplemente cambiarla a:

func description() -> String {
    return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}
Apfelsaft
fuente
Creo que el ejemplo sería un poco mejor si lo cambiaras para usarloif let age = age { return ""} else { return "" }
kubi
13
+1 para:Many Swift beginners think var age: Int? means that age is an Int which may or may not have a value. But it means that age is an Optional which may or may not hold an Int.
lee
18

Un opcional significa que Swift no está completamente seguro de si el valor corresponde al tipo: por ejemplo, Int? significa que Swift no está completamente seguro de si el número es un Int.

Para eliminarlo, hay tres métodos que puede emplear.

1) Si está absolutamente seguro del tipo, puede usar un signo de exclamación para forzarlo a desenvolverlo, así:

// Here is an optional variable:

var age: Int?

// Here is how you would force unwrap it:

var unwrappedAge = age!

Si fuerza desenvolver un opcional y es igual a nulo, puede encontrar este error de bloqueo:

ingrese la descripción de la imagen aquí

Esto no es necesariamente seguro, por lo que aquí hay un método que podría evitar fallas en caso de que no esté seguro del tipo y valor:

Los métodos 2 y tres protegen contra este problema.

2) El opcional implícitamente desenvuelto

 if let unwrappedAge = age {

 // continue in here

 }

Tenga en cuenta que el tipo sin envolver ahora es Int , en lugar de Int? .

3) La declaración de guardia

 guard let unwrappedAge = age else { 
   // continue in here
 }

Desde aquí, puede seguir adelante y usar la variable no envuelta. Asegúrese de forzar el desenvolvimiento (con un!), Si está seguro del tipo de variable.

¡Buena suerte con tu proyecto!

GJZ
fuente
1
usando la edad de retorno! = nil? "(nombre) tiene (edad!) años". : "(nombre) oculta su edad".
leedream
1
Me salvó un dolor de cabeza con un problema que tenía. No puedo agradecerles lo suficiente.
Bruno Recillas
8

Para fines de prueba / depuración, a menudo quiero generar opcionales como cadenas sin tener que probar siempre los nilvalores, por lo que creé un operador personalizado.

Mejoré las cosas aún más después de leer esta respuesta en otra pregunta .

fileprivate protocol _Optional {
    func unwrappedString() -> String
}

extension Optional: _Optional {
    fileprivate func unwrappedString() -> String {
        switch self {
        case .some(let wrapped as _Optional): return wrapped.unwrappedString()
        case .some(let wrapped): return String(describing: wrapped)
        case .none: return String(describing: self)
        }
    }
}

postfix operator ~? { }
public postfix func ~? <X> (x: X?) -> String {
    return x.unwrappedString
}

Obviamente, el operador (y sus atributos) se puede ajustar a su gusto, o puede convertirlo en una función. De todos modos, esto le permite escribir código simple como este:

var d: Double? = 12.34
print(d)     // Optional(12.34)
print(d~?)   // 12.34
d = nil
print(d~?)   // nil

La integración de la idea del protocolo del otro tipo hizo que esto incluso funcionara con opcionales anidados, lo que a menudo ocurre cuando se usa el encadenamiento opcional. Por ejemplo:

let i: Int??? = 5
print(i)              // Optional(Optional(Optional(5)))
print("i: \(i~?)")    // i: 5
Jeremy
fuente
1
A partir de Swift 3, también puede utilizar la función de biblioteca estándar de Swift debugPrint (_: separator: terminator :). Sin embargo, esto imprimirá la cadena entre comillas dobles.
psmythirl
@psmythirl: ¡Es bueno saberlo! Sin embargo, a veces es posible que desee simplemente pasar o incrustar un opcional como Stringen otro lugar (por ejemplo, un mensaje de registro / error).
Jeremy
print (d como Any)
DawnSong
7

Actualizar

Simplemente use me.age ?? "Unknown age!". Funciona en 3.0.2.

Respuesta antigua

Sin forzar el desenvolvimiento (sin señal mach / crash si es nulo) otra buena forma de hacer esto sería:

(result["ip"] ?? "unavailable").description.

result["ip"] ?? "unavailable" debería tener trabajo también, pero no lo hace, no en 2.2 al menos

Por supuesto, reemplace "no disponible" con lo que más le convenga: "nulo", "no encontrado", etc.

Valentin Radu
fuente
4

Para desenvolver el uso opcional en age!lugar de age. Actualmente está imprimiendo un valor opcional que podría ser nil. Es por eso que se envolvió con Optional.

Kirsteins
fuente
4

En swift Optionales algo que puede ser nilen algunos casos. Si está 100% seguro de que a variabletendrá algún valor siempre y no devolverá nilel complemento! con la variable para forzar el desencadenamiento.

En otro caso, si no está muy seguro del valor, agregue un if letbloque o guardpara asegurarse de que el valor exista, de lo contrario, puede provocar un bloqueo.

Por if letbloque:

if let abc = any_variable {
 // do anything you want with 'abc' variable no need to force unwrap now.
}

por guard declaración:

guard es una estructura condicional para devolver el control si no se cumple la condición.

Prefiero usar guardover if letblock en muchas situaciones, ya que nos permite devolver el functionsi no existe un valor en particular. Como cuando hay una función donde una variable es integral para existir, podemos verificarla en la declaración de guardia y el retorno de ella no existe. es decir;

guard let abc = any_variable else { return }

Si la variable existe, podemos usar 'abc' en la función fuera del alcance de la guardia.

Muhammad Yawar Ali
fuente
3

agees de tipo opcional: Optional<Int>por lo que si lo compara con nil, devuelve falso cada vez si tiene un valor o si no lo tiene. Necesita desenvolver el opcional para obtener el valor.

En su ejemplo, no sabe si contiene algún valor, por lo que puede usar esto en su lugar:

if let myAge = age {
    // there is a value and it's currently undraped and is stored in a constant
}
else {
   // no value
}
Greg
fuente
3

Hice esto para imprimir el valor de la cadena (propiedad) desde otro controlador de vista.

ViewController.swift

var testString:NSString = "I am iOS Developer"

SecondViewController.swift

var obj:ViewController? = ViewController(nibName: "ViewController", bundle: nil)
print("The Value of String is \(obj!.testString)")

Resultado:

The Value of String is I am iOS Developer
iCodeAtApple
fuente
2
No debe forzar el desenvolver un opcional
E-Riddie
3

Mira la guarddeclaración:

for student in class {
    guard let age = student.age else { 
        continue 
     }
    // do something with age
}
Phil Hudson
fuente
3

Cuando tiene un valor predeterminado:

print("\(name) is \(age ?? 0) years old")

o cuando el nombre es opcional:

print("\(name ?? "unknown") is \(age) years old")

Arnón
fuente
1

Obtenía el Opcional ("Cadena") en mis celdas de vista de tabla.

La primera respuesta es genial. Y me ayudó a resolverlo. Esto es lo que hice, para ayudar a los novatos como yo.

Dado que estoy creando una matriz en mi objeto personalizado, sé que siempre tendrá elementos en la primera posición, por lo que puedo forzar su desenvolvimiento en otra variable. Luego use esa variable para imprimir, o en mi caso, establezca el texto de la celda de la vista de tabla.

let description = workout.listOfStrings.first!
cell.textLabel?.text = description

Parece tan simple ahora, pero me tomó un tiempo averiguarlo.

Joshua Dance
fuente
-1

Esta no es la respuesta exacta a esta pregunta, pero sí una de las razones de este tipo de problema. En mi caso, no pude eliminar Optional de una Cadena con "if let" y "guard let".

Por lo tanto, use AnyObject en lugar de Any para eliminar opcional de una cadena en rápido.

Consulte el enlace para obtener la respuesta.

https://stackoverflow.com/a/51356716/8334818

Pramod Más
fuente