ios - tutorial - test e2e




Wann sollte ich einen optionalen Wert mit nil vergleichen? (4)

Sehr oft müssen Sie Code wie den folgenden schreiben:

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

Dies scheint ein bisschen wortreich, und ich höre das auch mit dem ! Force Unwrap Operator kann unsicher sein und am besten vermieden werden. Gibt es eine bessere Möglichkeit, damit umzugehen?


Es ist fast immer unnötig zu überprüfen, ob ein optionales nicht gleich nil . So ziemlich das einzige Mal, dass Sie dies tun müssen, ist, wenn das Nichts das Einzige ist, über das Sie etwas wissen möchten - es ist Ihnen egal, was im Wert enthalten ist, nur, dass es nicht nil .

Unter den meisten anderen Umständen gibt es eine Abkürzung für Swift, mit der Sie die Aufgabe sicherer und präziser erledigen können.

Verwenden Sie den Wert, wenn er nicht nil

Anstatt von:

let s = "1"
let i = Int(s)

if i != nil {
    print(i! + 1)
}

Sie können verwenden, if let :

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

Sie können auch var :

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

Beachten Sie jedoch, dass es sich um eine lokale Kopie handelt. Änderungen an i sich nicht auf den Wert im Original aus.

Sie können mehrere Optionen in einer einzigen Packung auspacken, und spätere können von früheren abhängen:

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

Sie können den nicht umbrochenen Werten auch where Klauseln hinzufügen:

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

Ersetzen Sie nil durch einen Standardwert

Anstatt von:

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

oder:

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

Sie können den Null-Koaleszenz-Operator verwenden, ?? :

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

Gleichsetzen eines optionalen mit einem nicht optionalen

Anstatt von:

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

Sie können überprüfen, ob die optionalen Werte nicht-optionalen Werten entsprechen:

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

Dies funktioniert auch mit Vergleichen:

if i < 5 { }

nil ist immer gleich anderen nil und ist kleiner als jeder Nicht- nil Wert.

Achtung! Hier kann es Fallstricke geben:

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

Aufrufen einer Methode (oder Lesen einer Eigenschaft) für eine optionale

Anstatt von:

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

Sie können optional Verkettung verwenden, ?. :

let j = i?.successor()

Beachten Sie, dass j jetzt auch optional ist, um das fatalError Szenario zu berücksichtigen. Später können Sie eine der anderen Techniken in dieser Antwort verwenden, um die Optionalität von j zu behandeln, aber Sie können das tatsächliche Auspacken Ihrer Optionen oft viel später oder manchmal gar nicht verschieben.

Wie der Name schon sagt, können Sie sie verketten, so dass Sie schreiben können:

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

Optionale Verkettung funktioniert auch mit Indizes:

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

und Funktionen:

let dictOfFuncs: [String:(Int,Int)->Int] = [
      "add":(+),
      "subtract":(-)
]

dictOfFuncs["add"]?(1,1)  // returns {Some 2}

Zuweisen zu einer Eigenschaft auf einem optionalen

Anstatt von:

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

Sie können durch eine optionale Kette zuweisen:

splitViewController?.delegate = self

Nur wenn splitViewController nicht nil erfolgt die Zuweisung.

Verwenden des Werts, wenn er nicht gleich nil , oder Bailing (neu in Swift 2.0)

Manchmal möchten Sie in einer Funktion eine kurze Codezeile schreiben, um ein optionales nil zu nil Wenn dies nicht der nil , beenden Sie die Funktion frühzeitig, andernfalls fahren Sie fort.

Sie könnten dies so schreiben:

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

oder um das gewaltsame Auspacken zu vermeiden, wie folgt:

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

Es ist jedoch viel angenehmer, den Fehlerbehandlungscode bei der Überprüfung an oberster Stelle zu halten. Dies kann auch zu einem unangenehmen Nisten führen (die "Pyramide des Untergangs").

Stattdessen können Sie guard , was wie ein 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)
}

Der else Teil muss den Gültigkeitsbereich des geschützten Werts verlassen, z. B. return oder fatalError , um fatalError , dass der geschützte Wert für den Rest des Gültigkeitsbereichs gültig ist.

guard ist nicht auf den Funktionsumfang beschränkt. Zum Beispiel das Folgende:

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

    print(i+1, appendNewline: false)
}

druckt 321 .

Nicht-Null-Elemente in einer Sequenz durchlaufen (neu in Swift 2.0)

Wenn Sie eine Folge von Optionen haben, können Sie for case let _? Alle nicht optionalen Elemente durchlaufen:

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

druckt 321 . Hierbei wird die Mustervergleichssyntax für ein optionales verwendet. Welcher Variablenname wird gefolgt von ? .

Sie können diesen Mustervergleich auch in switch Anweisungen verwenden:

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

Schleifen, bis eine Funktion nil zurückgibt

Ähnlich wie if let können Sie auch schreiben, while let und loopen, bis nil :

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

Sie können auch schreiben, while var (ähnliche Vorsichtsmaßnahmen wie if var ).

where Klauseln funktionieren auch hier (und beenden die Schleife, anstatt zu überspringen):

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

Übergeben eines optionalen Werts an eine Funktion, die einen nicht optionalen Wert annimmt und ein Ergebnis zurückgibt

Anstatt von:

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

Sie können den optionalen Kartenoperator verwenden:

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

Dies ist der optionalen Verkettung sehr ähnlich, aber wenn Sie den nicht optionalen Wert als Argument an die Funktion übergeben müssen. Wie bei der optionalen Verkettung ist das Ergebnis optional.

Das ist schön, wenn man sowieso ein optionales möchte. Beispielsweise entspricht reduce1 , verwendet jedoch den ersten Wert als Startwert und gibt ein optionales zurück, falls das Array leer ist. Sie könnten es so schreiben (mit dem Schlüsselwort guard von früher):

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

Stattdessen können Sie die Eigenschaft .first und .first zurückgeben:

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

Übergeben einer Option an eine Funktion, die eine Option annimmt und ein Ergebnis zurückgibt, um ärgerliche Doppeloptionen zu vermeiden

Manchmal möchten Sie etwas Ähnliches wie map , aber die Funktion, die Sie selbst aufrufen möchten, gibt optional zurück. Zum Beispiel:

// 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) }

Aber jetzt ist idx vom Typ Int?? , eine Doppeloption. Stattdessen können Sie flatMap , das das Ergebnis zu einem einzigen optionalen flatMap "glättet":

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

Ich denke, Sie sollten zum Swift-Programmierbuch zurückkehren und lernen, wozu diese Dinge gut sind. ! wird verwendet, wenn Sie absolut sicher sind, dass das optionale nicht gleich null ist. Da Sie erklärt haben, dass Sie absolut sicher sind, stürzt es ab, wenn Sie sich irren. Welches ist völlig beabsichtigt. Es ist "unsicher und am besten zu vermeiden" in dem Sinne, dass Aussagen in Ihrem Code "unsicher und am besten zu vermeiden" sind. Zum Beispiel:

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

Das ! ist absolut sicher. Es sei denn, es gibt einen großen Fehler in Ihrem Code, wie das versehentliche Schreiben (ich hoffe, Sie erkennen den Fehler)

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

In diesem Fall stürzt Ihre App möglicherweise ab, Sie ermitteln, warum sie abstürzt, und Sie beheben den Fehler. Genau dafür gibt es den Absturz. Ein Ziel in Swift ist, dass Ihre App offensichtlich korrekt funktionieren sollte. Da Swift dies jedoch nicht erzwingen kann, wird erzwungen, dass Ihre App entweder korrekt funktioniert oder wenn möglich abstürzt, sodass Fehler beseitigt werden, bevor die App ausgeliefert wird.


Sie gibt es einen Weg. Es wird als optionale Verkettung bezeichnet . Aus der Dokumentation:

Das optionale Verketten ist ein Prozess zum Abfragen und Aufrufen von Eigenschaften, Methoden und Indizes für ein optionales Element, das derzeit möglicherweise null ist. Wenn die Option einen Wert enthält, ist der Aufruf der Eigenschaft, der Methode oder des Index erfolgreich. Wenn das optionale Argument "nil" lautet, gibt der Aufruf der Eigenschaft, Methode oder des Indexes "nil" zurück. Mehrere Abfragen können miteinander verkettet werden, und die gesamte Kette schlägt ordnungsgemäß fehl, wenn ein Glied in der Kette nicht vorhanden ist.

Hier ist ein Beispiel

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

Den vollständigen Artikel finden Sie hier .


Wir können optional Bindung verwenden.

var x:Int?

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




swift2