Stampa variabile opzionale


118

Sto provando con queste righe di codice

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())

Il codice sopra produce quanto segue

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

La mia domanda è perché c'è Opzionale (18) lì, come posso rimuovere l'opzionale e solo la stampa

Daniel is 18 years old.

Risposte:


190

Devi capire cos'è veramente un Opzionale. Molti principianti di Swift pensano var age: Int?che l'età sia un Int che può avere o meno un valore. Ma significa che l'età è un Opzionale che può contenere o meno un Int.

All'interno della tua description()funzione non stampi l'Int, ma invece stampi l'Opzionale. Se vuoi stampare l'Int devi scartare l'Opzionale. Puoi utilizzare "rilegatura facoltativa" per scartare un facoltativo:

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

Se sei sicuro che l'Operativo contenga un oggetto, puoi utilizzare lo "scartamento forzato":

let a = age!

O nel tuo esempio, poiché hai già un test per zero nella funzione di descrizione, puoi semplicemente cambiarlo in:

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

Penso che l'esempio sarebbe leggermente migliore se lo cambiassi per usareif let age = age { return ""} else { return "" }
kubi

13
+1 per: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 opzionale significa che Swift non è del tutto sicuro che il valore corrisponda al tipo: ad esempio, Int? significa che Swift non è del tutto sicuro che il numero sia un Int.

Per rimuoverlo, ci sono tre metodi che potresti utilizzare.

1) Se sei assolutamente sicuro del tipo, puoi utilizzare un punto esclamativo per forzare lo scartamento, in questo modo:

// Here is an optional variable:

var age: Int?

// Here is how you would force unwrap it:

var unwrappedAge = age!

Se forzi lo scartamento di un opzionale ed è uguale a zero, potresti riscontrare questo errore di crash:

inserisci qui la descrizione dell'immagine

Questo non è necessariamente sicuro, quindi ecco un metodo che potrebbe impedire l'arresto anomalo nel caso in cui non si sia certi del tipo e del valore:

I metodi 2 e 3 proteggono da questo problema.

2) L'opzione Implicitamente non confezionata

 if let unwrappedAge = age {

 // continue in here

 }

Si noti che il tipo da scartare è ora Int , anziché Int? .

3) La dichiarazione di guardia

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

Da qui, puoi andare avanti e utilizzare la variabile scartata. Assicurati solo di forzare lo scartamento (con un!), Se sei sicuro del tipo di variabile.

Buona fortuna con il vostro progetto!


1
utilizzando return age! = nil? "(nome) ha (età!) anni." : "(nome) nasconde la sua età."
leedream

1
Mi ha salvato il mal di testa con un problema che avevo. Non posso ringraziarti abbastanza.
Bruno Recillas

8

A scopo di test / debug, spesso desidero visualizzare gli optionals come stringhe senza dover sempre testare i nilvalori, quindi ho creato un operatore personalizzato.

Ho migliorato ulteriormente le cose dopo aver letto questa risposta in un'altra domanda .

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
}

Ovviamente l'operatore (e i suoi attributi) può essere modificato a tuo piacimento, oppure potresti renderlo una funzione. Ad ogni modo, questo ti consente di scrivere codice semplice come questo:

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

L'integrazione dell'idea del protocollo dell'altro ha fatto sì che funzionasse anche con gli optionals annidati, che spesso si verificano quando si usa il concatenamento opzionale. Per esempio:

let i: Int??? = 5
print(i)              // Optional(Optional(Optional(5)))
print("i: \(i~?)")    // i: 5

1
A partire da Swift 3 puoi anche utilizzare la funzione di libreria standard di Swift debugPrint (_: separator: terminator :). Ciò tuttavia stamperà la stringa tra virgolette doppie.
psmythirl

@psmythirl: Buono a sapersi! Tuttavia, a volte potresti voler semplicemente passare o incorporare un optional come Stringaltrove (ad esempio un messaggio di log / errore).
Jeremy

print (d as Any)
DawnSong

7

Aggiornare

Basta usare me.age ?? "Unknown age!". Funziona in 3.0.2.

Vecchia risposta

Senza forzare lo sblocco (nessun segnale mach / crash se nullo) un altro bel modo per farlo sarebbe:

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

result["ip"] ?? "unavailable" dovrebbe funzionare anche lui, ma non è così, almeno non nella 2.2

Ovviamente, sostituisci "non disponibile" con quello che ti si addice: "nil", "non trovato" ecc


4

Per scartare l'uso opzionale al age!posto di age. Attualmente stai stampando un valore opzionale che potrebbe essere nil. Ecco perché è avvolto con Optional.


4

In swift Optionalè qualcosa che può essere nilin alcuni casi. Se sei sicuro al 100% che a variableavrà sempre un valore e non restituirà nill'aggiunta !con la variabile per forzare lo scartamento.

In altri casi, se non sei molto sicuro del valore, aggiungi un if letblocco o guardassicurati che il valore esista altrimenti potrebbe causare un arresto anomalo.

Per if letblocco:

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

Per guarddichiarazione:

guard è una struttura condizionale per restituire il controllo se la condizione non è soddisfatta.

Preferisco usare guardil if letblocco in molte situazioni in quanto ci consente di restituire functionse un particolare valore non esiste. Come quando c'è una funzione in cui una variabile è parte integrante dell'esistenza, possiamo controllarla nell'istruzione guard e restituirla non esiste. ie;

guard let abc = any_variable else { return }

Se la variabile esiste, possiamo usare 'abc' nella funzione al di fuori dell'ambito della guardia.


3

ageè di tipo opzionale: Optional<Int>quindi se lo confronti con zero restituisce false ogni volta se ha un valore o se non lo ha. Devi scartare l'opzionale per ottenere il valore.

Nel tuo esempio non sai se contiene alcun valore, quindi puoi usare questo invece:

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

3

L'ho fatto per stampare il valore della stringa (proprietà) da un altro controller di visualizzazione.

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)")

Risultato:

The Value of String is I am iOS Developer

2
Non dovresti forzare a scartare un optional
E-Riddie

3

Controlla la guarddichiarazione:

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

3

Quando si dispone di un valore predefinito:

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

o quando il nome è facoltativo:

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


1

Stavo ottenendo il facoltativo ("String") nelle mie celle tableview.

La prima risposta è ottima. E mi ha aiutato a capirlo. Ecco cosa ho fatto, per aiutare i debuttanti là fuori come me.

Dato che sto creando un array nel mio oggetto personalizzato, so che avrà sempre gli elementi nella prima posizione, quindi posso forzare lo scartamento in un'altra variabile. Quindi usa quella variabile per stampare o, nel mio caso, impostare il testo della cella tableview.

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

Sembra così semplice ora, ma mi ci è voluto un po 'per capirlo.


-1

Questa non è la risposta esatta a questa domanda, ma una delle ragioni di questo tipo di problema. Nel mio caso, non sono stato in grado di rimuovere Optional da una stringa con "if let" e "guard let".

Quindi usa AnyObject invece di Any per rimuovere optional da una stringa in swift.

Si prega di fare riferimento al collegamento per la risposta.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.