Oft müssen Sie folgenden Code schreiben:
if someOptional != nil {
// do something with the unwrapped someOptional e.g.
someFunction(someOptional!)
}
Dies scheint etwas wortreich zu sein, und ich höre auch, dass die Verwendung des Unlock-Operators !
unsicher und am besten vermieden werden kann. Gibt es einen besseren Weg, um damit umzugehen?
Es ist fast immer unnötig zu prüfen, ob ein optionales nicht nil
ist. Das ist so ziemlich das einzige Mal, dass Sie dies tun müssen, wenn die nil
-Ness die einzige Sache ist, über die Sie wissen möchten - Sie Es ist mir egal, was im Wert ist, nur dass es nicht nil
ist.
Unter den meisten anderen Umständen gibt es eine Abkürzung für Swift, die die Aufgabe in if
sicherer und präziser für Sie erledigen kann.
Verwenden des Werts, wenn er nicht nil
ist
Anstatt von:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
sie können if let
verwenden:
if let i = Int(s) {
print(i + 1)
}
Sie können auch var
verwenden:
if var i = Int(s) {
print(++i) // prints 2
}
beachten Sie jedoch, dass i
eine lokale Kopie ist. Änderungen an i
wirken sich nicht auf den Wert im Original aus Optional.
Sie können mehrere Optionen in einem einzigen if let
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 von 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 nil
s und ist kleiner als jeder andere Wert als nil
.
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 ein optionales
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 die optionale Verkettung ?.
verwenden:
let j = i?.successor()
Beachten Sie, dass j
jetzt auch optional ist, um das Szenario fatalError
zu berücksichtigen. Später können Sie eine der anderen Techniken in dieser Antwort verwenden, um die Optionalität von j
zu behandeln. Oft können Sie jedoch das eigentliche Auspacken Ihrer Optionen bis 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}
Einer Eigenschaft auf einem optionalen zuweisen
Anstatt von:
if splitViewController != nil {
splitViewController!.delegate = self
}
sie können durch eine optionale Kette zuweisen:
splitViewController?.delegate = self
Nur wenn splitViewController
nicht -nil
ist, erfolgt die Zuweisung.
Verwenden des Werts, wenn es nicht nil
oder Bailing ist (neu in Swift 2.0)
Manchmal möchten Sie in einer Funktion einen kurzen Code schreiben, um ein optionales Element zu aktivieren. Wenn dies nil
ist, 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 ganz oben zu belassen. Dies kann auch zu einem unangenehmen Nisten führen (die "Pyramide des Schicksals").
Stattdessen können Sie guard
verwenden, was einem if not let
Ähnelt:
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 Teil else
muss den Bereich des geschützten Werts verlassen, z. a return
oder fatalError
, um sicherzustellen, dass der geschützte Wert für den Rest des Bereichs 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 mit 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 Element verwendet. Dies ist ein Variablenname, 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 bei if let
Können Sie auch while let
Schreiben und eine Schleife ausführen, bis nil
:
while let line = readLine() {
print(line)
}
Sie können auch while var
Schreiben (ähnliche Vorsichtsmaßnahmen wie bei 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 optional den map
-Operator 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 ist reduce1
Wie reduce
, verwendet jedoch den ersten Wert als Startwert und gibt ein optionales zurück, falls das Array leer ist. Du könntest es so schreiben (mit dem guard
Schlüsselwort 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
map
und Folgendes zurückgeben:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}
Übergeben eines optionalen Werts an eine Funktion, die einen optionalen Wert annimmt und ein Ergebnis zurückgibt, um lästige doppelte Optionen zu vermeiden
Manchmal möchten Sie etwas Ähnliches wie map
, aber die Funktion, die Sie selbst aufrufen möchten , gibt ein optionales 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 doppelte Option. Stattdessen können Sie flatMap
verwenden, wodurch das Ergebnis zu einem einzigen optionalen Ergebnis „geglättet“ wird:
let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
Ich denke, du solltest zum Swift-Programmierbuch zurückkehren und lernen, wozu diese Dinge gehören. ! wird verwendet, wenn Sie absolut sicher sind, dass das optionale Element nicht 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 vermieden" in dem Sinne, dass Behauptungen in Ihrem Code "unsicher und am besten vermieden" sind. Zum Beispiel:
if someOptional != nil {
someFunction(someOptional!)
}
Das ! ist absolut sicher. Es sei denn, es liegt ein großer Fehler in Ihrem Code vor, z. B. wenn Sie versehentlich schreiben (ich hoffe, Sie erkennen den Fehler).
if someOptional != nil {
someFunction(SomeOptional!)
}
in diesem Fall kann Ihre App abstürzen, Sie untersuchen, warum sie abstürzt, und Sie beheben den Fehler. Genau dafür gibt es den Absturz. Ein Ziel von Swift ist es, dass Ihre App offensichtlich ordnungsgemäß funktionieren sollte. Da Swift dies jedoch nicht erzwingen kann, erzwingt es, dass Ihre App entweder ordnungsgemäß funktioniert oder abstürzt, wenn dies möglich ist. Bugs werden daher vor der Auslieferung der App entfernt.
Sie gibt es einen Weg. Es heißt Optionale Verkettung . Aus der Dokumentation:
Optionale Verkettung ist ein Prozess zum Abfragen und Aufrufen von Eigenschaften Methoden und Subskripte auf einem optionalen, der derzeit null sein kann. Ob Das optionale Element enthält einen Wert, die Eigenschaft, die Methode oder den Indexaufruf gelingt es; Wenn die Option null ist, gilt die Eigenschaft, Methode oder das Index call gibt null zurück. Mehrere Abfragen können miteinander verkettet werden, und die Die gesamte Kette fällt ordnungsgemäß aus, wenn ein Glied in der Kette gleich Null 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."
Sie können den vollständigen Artikel hier überprüfen.
Nach langem Nachdenken und Nachforschen habe ich den einfachsten Weg gefunden, ein optionales Zubehör auszupacken:
Erstellen Sie eine neue Swift-Datei und nennen Sie sie UnwrapOperator.Swift
Fügen Sie den folgenden Code in die Datei ein:
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
}
Der obige Code hat nun 2 Operator [Ein Präfix und ein Postfix] erstellt.
Die Erklärung ist einfach: Die Operatoren geben den constructor-Wert zurück, wenn sie in der Variablen nil erhalten, ansonsten der enthaltene Wert in der Variablen.
Unten ist das Anwendungsbeispiel:
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
Unten ist das Beispiel, wenn variable nil enthält:
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
Sie haben nun die Wahl, welchen Operator Sie verwenden, und beide dienen demselben Zweck.
Wir können eine optionale 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
}