Drukowanie zmiennej opcjonalnej

118

Próbuję z tymi liniami kodu

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

Powyższy kod daje następujące wyniki

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

Moje pytanie brzmi, dlaczego jest tam opcja (18), jak mogę usunąć opcjonalną i tylko drukowanie

Daniel is 18 years old.
Daniel Adinugroho
źródło

Odpowiedzi:

190

Musisz zrozumieć, czym naprawdę jest opcja. Wielu początkujących Swift uważa var age: Int?, że wiek jest wartością Int, która może mieć wartość lub nie. Ale oznacza to, że wiek jest opcjonalny, który może zawierać Int.

Wewnątrz description()funkcji nie drukujesz Int, ale zamiast tego drukujesz Optional. Jeśli chcesz wydrukować Int, musisz rozpakować plik Optional. Możesz użyć „opcjonalnego wiązania”, aby rozpakować opcjonalne:

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

Jeśli jesteś pewien, że Opcjonalne przechowuje obiekt, możesz użyć „wymuszonego rozpakowywania”:

let a = age!

Lub w Twoim przykładzie, ponieważ masz już test na zero w funkcji opisu, możesz go po prostu zmienić na:

func description() -> String {
    return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}
Apfelsaft
źródło
Myślę, że przykład byłby nieco lepszy, gdybyś zmienił go na używanieif let age = age { return ""} else { return "" }
kubi
13
+1 za: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

Opcjonalne oznacza, że ​​Swift nie jest całkowicie pewien, czy wartość odpowiada typowi: na przykład Int? oznacza, że ​​Swift nie jest całkowicie pewien, czy liczba jest liczbą Int.

Aby go usunąć, możesz zastosować trzy metody.

1) Jeśli jesteś absolutnie pewien typu, możesz użyć wykrzyknika, aby wymusić jego rozpakowanie, na przykład:

// Here is an optional variable:

var age: Int?

// Here is how you would force unwrap it:

var unwrappedAge = age!

Jeśli wymusisz rozpakowanie opcjonalnego i jest on równy zero, możesz napotkać ten błąd awarii:

wprowadź opis obrazu tutaj

Niekoniecznie jest to bezpieczne, więc oto metoda, która może zapobiec awariom w przypadku braku pewności co do typu i wartości:

Metody 2 i 3 zabezpieczają przed tym problemem.

2) niejawnie nieopakowany opcjonalny

 if let unwrappedAge = age {

 // continue in here

 }

Zauważ, że rozpakowany typ to teraz Int , a nie Int? .

3) Oświadczenie strażnika

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

Stąd możesz iść dalej i użyć nieopakowanej zmiennej. Upewnij się, że wymuszasz rozpakowywanie (za pomocą!), Jeśli jesteś pewien typu zmiennej.

Powodzenia w Twoim projekcie!

GJZ
źródło
1
przy użyciu wieku powrotu! = nil? „(imię) ma (wiek!) lat”. : „(imię) ukrywa swój wiek”.
leedream
1
Uratował mi ból głowy z powodu problemu, który miałem. Nie mogę ci wystarczająco podziękować.
Bruno Recillas
8

Do celów testowania / debugowania często chcę wyprowadzać opcje w postaci ciągów bez konieczności testowania nilwartości, dlatego utworzyłem niestandardowy operator.

Po przeczytaniu tej odpowiedzi w innym pytaniu poprawiłem się jeszcze bardziej .

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
}

Oczywiście operator (i jego atrybuty) można dostosować do własnych upodobań lub zamiast tego można uczynić go funkcją. W każdym razie umożliwia to pisanie prostego kodu, takiego jak ten:

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

Integracja koncepcji protokołu drugiego faceta sprawiła, że ​​działa to nawet z zagnieżdżonymi opcjami, które często występują podczas korzystania z opcjonalnego łączenia. Na przykład:

let i: Int??? = 5
print(i)              // Optional(Optional(Optional(5)))
print("i: \(i~?)")    // i: 5
Jeremy
źródło
1
Począwszy od Swift 3 możesz również użyć funkcji debugPrint standardowej biblioteki Swift (_: separator: terminator :). Spowoduje to jednak wydrukowanie ciągu w podwójnych cudzysłowach.
psmythirl
@psmythirl: Dobrze wiedzieć! Jednak czasami możesz chcieć po prostu przekazać lub osadzić opcjonalny element Stringw innym miejscu (np. Dziennik / komunikat o błędzie).
Jeremy
print (d as Any)
DawnSong
7

Aktualizacja

Po prostu użyj me.age ?? "Unknown age!". Działa w wersji 3.0.2.

Stara odpowiedź

Bez rozwijania siły (bez sygnału mach / crash, jeśli zero), innym dobrym sposobem na zrobienie tego byłoby:

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

result["ip"] ?? "unavailable" powinien też działać, ale tak się nie dzieje, przynajmniej nie w wersji 2.2

Oczywiście, zastąp „niedostępny” tym, co ci odpowiada: „nil”, „nie znaleziono” itp

Valentin Radu
źródło
4

Aby rozpakować opcjonalne, użyj age!zamiast age. Obecnie drukujesz opcjonalną wartość, która może być nil. Właśnie dlatego jest opakowany Optional.

Kirsteins
źródło
4

W szybkim Optionaljest coś, co może być nilw niektórych przypadkach. Jeśli masz 100% pewności, że a variablezawsze będzie miał jakąś wartość i nie zwróci niladd !ze zmienną, aby wymusić jej rozpakowanie.

W innym przypadku, jeśli nie masz pewności co do wartości, dodaj if letblok lub guardupewnij się, że wartość istnieje, w przeciwnym razie może to spowodować awarię.

Dla if letbloku:

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

Do guardoświadczenia:

guard jest strukturą warunkową zwracającą kontrolę, jeśli warunek nie zostanie spełniony.

W wielu sytuacjach wolę używać guardover if letblock, ponieważ pozwala nam to zwrócić, functionjeśli określona wartość nie istnieje. Tak jak wtedy, gdy istnieje funkcja, w której zmienna jest całka, aby istnieć, możemy sprawdzić ją w instrukcji guard i jej zwrot nie istnieje. to znaczy;

guard let abc = any_variable else { return }

Jeśli zmienna istnieje, możemy użyć 'abc' w funkcji poza zasięgiem ochrony.

Muhammad Yawar Ali
źródło
3

agejest typem opcjonalnym: Optional<Int>więc jeśli porównasz go do nil, zwraca on fałsz za każdym razem, gdy ma wartość lub nie ma. Aby uzyskać wartość, musisz rozpakować opcjonalne.

W Twoim przykładzie nie wiesz, czy zawiera jakąkolwiek wartość, więc możesz użyć tego zamiast tego:

if let myAge = age {
    // there is a value and it's currently undraped and is stored in a constant
}
else {
   // no value
}
Greg
źródło
3

Zrobiłem to, aby wydrukować wartość ciągu (właściwości) z innego kontrolera widoku.

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

Wynik:

The Value of String is I am iOS Developer
iCodeAtApple
źródło
2
Nie powinieneś wymuszać rozpakowywania opcjonalnego
E-Riddie
3

Sprawdź guardoświadczenie:

for student in class {
    guard let age = student.age else { 
        continue 
     }
    // do something with age
}
Phil Hudson
źródło
3

Mając wartość domyślną:

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

lub gdy nazwa jest opcjonalna:

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

arnon
źródło
1

W moich komórkach widoku tabeli pojawiał się opcjonalny („ciąg”).

Pierwsza odpowiedź jest świetna. I pomógł mi to rozgryźć. Oto, co zrobiłem, aby pomóc nowicjuszom takim jak ja.

Ponieważ tworzę tablicę w moim obiekcie niestandardowym, wiem, że zawsze będzie ona zawierała elementy na pierwszej pozycji, więc mogę wymusić rozpakowanie jej do innej zmiennej. Następnie użyj tej zmiennej do wydrukowania lub w moim przypadku ustaw na tekst komórki widoku tabeli.

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

Wydaje się teraz takie proste, ale zajęło mi trochę czasu, zanim to rozgryzłem.

Joshua Dance
źródło
-1

To nie jest dokładna odpowiedź na to pytanie, ale jeden z powodów tego rodzaju problemów. W moim przypadku nie udało mi się usunąć Optional z String z „if let” i „guard let”.

Więc użyj AnyObject zamiast Any, aby usunąć opcjonalne z ciągu w swift.

Proszę odnieść się do linku do odpowiedzi.

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

Pramod Więcej
źródło