516 Stimmen

Warum erstellen Sie "Implicitly Unwrapped Optionals", da dies bedeutet, dass Sie wissen, dass es einen Wert gibt?

Warum würden Sie ein "Implicitly Unwrapped Optional" erstellen, anstatt einfach eine normale Variable oder Konstante zu erstellen? Wenn Sie wissen, dass es erfolgreich unwrapped werden kann, warum erstellen Sie dann überhaupt ein Optional? Zum Beispiel, warum ist das:

let someString: String! = "dies ist der String"

nützlicher als:

let someString: String = "dies ist der String"

Wenn "Optionale anzeigen, dass eine Konstante oder Variable 'keinen Wert' haben kann", aber "manchmal aus der Struktur eines Programms klar ist, dass ein Optional immer einen Wert haben wird, nachdem dieser Wert zum ersten Mal festgelegt wurde", warum macht man es dann überhaupt zu einem Optional? Wenn Sie wissen, dass ein Optional immer einen Wert haben wird, macht das es dann nicht eigentlich nicht optional?

465voto

drewag Punkte 91541

Bevor ich die Anwendungsfälle für implizit entpackte Optionals beschreiben kann, sollten Sie bereits verstehen, was Optionals und implizit entpackte Optionals in Swift sind. Wenn nicht, empfehle ich Ihnen, zuerst meinen Artikel über Optionals zu lesen.

Wann man ein implizit entpacktes Optional verwenden sollte

Es gibt zwei Hauptgründe, warum man ein implizit entpacktes Optional erstellen würde. Beide haben damit zu tun, eine Variable zu definieren, auf die niemals mit nil zugegriffen wird, da sonst der Swift-Compiler immer verlangt, dass Sie ein Optional explizit entpacken.

1. Eine Konstante, die während der Initialisierung nicht definiert werden kann

Jede Mitgliedskonstante muss einen Wert haben, wenn die Initialisierung abgeschlossen ist. Manchmal kann eine Konstante während der Initialisierung nicht mit ihrem korrekten Wert initialisiert werden, aber es kann dennoch garantiert werden, dass sie vor dem Zugriff einen Wert hat.

Die Verwendung einer Optional-Variable umgeht dieses Problem, da ein Optional automatisch mit nil initialisiert wird und der Wert, den es irgendwann enthalten wird, trotzdem unveränderlich bleibt. Es kann jedoch lästig sein, ständig eine Variable zu entpacken, von der Sie sicher wissen, dass sie nicht nil ist. Implizit entpackte Optionals erzielen die gleichen Vorteile wie ein Optional mit dem zusätzlichen Vorteil, dass man es nicht überall explizit entpacken muss.

Ein großartiges Beispiel hierfür ist, wenn eine Mitgliedsvariable in einer UIView-Unterklasse nicht initialisiert werden kann, bis die Ansicht geladen ist:

class MyView: UIView {
    @IBOutlet var button: UIButton!
    var buttonOriginalWidth: CGFloat!

    override func awakeFromNib() {
        self.buttonOriginalWidth = self.button.frame.size.width
    }
}

Hier können Sie die ursprüngliche Breite des Buttons nicht berechnen, bis die Ansicht geladen ist, aber Sie wissen, dass awakeFromNib aufgerufen wird, bevor eine andere Methode auf der Ansicht (außer der Initialisierung) aufgerufen wird. Anstatt den Wert sinnlos in Ihrer Klasse überall explizit zu entpacken, können Sie ihn als implizit entpacktes Optional deklarieren.

2. Wenn Ihre App sich nicht von einer Variable erholen kann, die nil ist

Das sollte äußerst selten sein, aber wenn Ihre App nicht weiterlaufen kann, wenn eine Variable nil ist, wenn darauf zugegriffen wird, wäre es Zeitverschwendung, sie auf nil zu prüfen. Normalerweise würden Sie bei einer Bedingung, die unbedingt wahr sein muss, damit Ihre App weiterläuft, ein assert verwenden. Ein implizit entpacktes Optional hat bereits eine Überprüfung auf nil eingebaut. Selbst dann ist es oft gut, das Optional zu entpacken und ein präziseres assert zu verwenden, wenn es nil ist.

Wann man ein implizit entpacktes Optional nicht verwenden sollte

1. Faul berechnete Mitgliedsvariablen

Manchmal haben Sie eine Mitgliedsvariable, die niemals nil sein sollte, aber sie kann während der Initialisierung nicht auf den richtigen Wert gesetzt werden. Eine Lösung wäre die Verwendung eines implizit entpackten Optionals, aber eine bessere Möglichkeit ist die Verwendung einer Lazy-Variable:

class FileSystemItem {
}

class Directory : FileSystemItem {
    lazy var contents : [FileSystemItem] = {
        var loadedContents = [FileSystemItem]()
        // Inhalt laden und an loadedContents anhängen
        return loadedContents
    }()
}

Jetzt wird die Mitgliedsvariable contents nicht initialisiert, bis sie zum ersten Mal aufgerufen wird. Dies gibt der Klasse die Möglichkeit, in den richtigen Zustand zu gelangen, bevor der anfängliche Wert berechnet wird.

Hinweis: Dies scheint sich vielleicht mit Punkt #1 oben zu widersprechen. Es gibt jedoch einen wichtigen Unterschied zu beachten. Die buttonOriginalWidth oben muss während viewDidLoad gesetzt werden, um zu verhindern, dass jemand die Breite der Schaltfläche ändert, bevor auf die Eigenschaft zugegriffen wird.

2. Überall sonst

Im Allgemeinen sollten implizit entpackte Optionals vermieden werden, denn wenn sie irrtümlich verwendet werden, wird Ihre gesamte App abstürzen, wenn sie aufgerufen wird, während sie nil ist. Wenn Sie jemals unsicher sind, ob eine Variable nil sein kann, verwenden Sie immer standardmäßig ein normales Optional. Eine Variable zu entpacken, die niemals nil ist, tut sicherlich nicht sehr weh.

132voto

Catfish_Man Punkte 40261

Betrachten Sie den Fall eines Objekts, das während seiner Konstruktion und Konfiguration möglicherweise nil-Eigenschaften haben kann, aber danach unveränderlich und nicht-nil ist (NSImage wird oft so behandelt, obwohl es in seinem Fall manchmal nützlich ist, zu mutieren). Implizit entpackte Optionals würden den Code erheblich aufräumen, mit relativ geringem Sicherheitsverlust (solange die eine Garantie besteht, wäre es sicher).

(Bearbeiten) Um es klar zu stellen: Reguläre Optionals sind fast immer vorzuziehen.

56voto

n8gray Punkte 4939

Implizit entpackte Optionals sind nützlich, um ein Attribut als nicht optional darzustellen, wenn es unter der Oberfläche tatsächlich optional sein muss. Dies ist oft notwendig, um "den Knoten zu schließen" zwischen zwei verwandten Objekten, die jeweils eine Referenz auf das andere benötigen. Es ergibt Sinn, wenn keine dieser Referenzen tatsächlich optional ist, aber eine von ihnen nil sein muss, während das Paar initialisiert wird.

Zum Beispiel:

// Diese Klassen sind Kumpels, die nie ohne einander auskommen
class B {
    var name : String
    weak var myBuddyA : A!
    init(name : String) {
        self.name = name
    }
}

class A {
    var name : String
    var myBuddyB : B
    init(name : String) {
        self.name = name
        myBuddyB = B(name:"\(name)'s buddy B")
        myBuddyB.myBuddyA = self
    }
}

var a = A(name:"Großes A")
println(a.myBuddyB.name)   // gibt "Big A's buddy B" aus

Jede B-Instanz sollte immer eine gültige myBuddyA-Referenz haben, daher möchten wir nicht, dass der Benutzer sie als optional behandelt, aber wir benötigen, dass sie optional ist, damit wir ein B konstruieren können, bevor wir eine A haben, auf die wir verweisen können.

ABER! Diese Art von gegenseitiger Referenzanforderung ist oft ein Hinweis auf eine enge Kopplung und schlechtes Design. Wenn Sie sich auf implizit entpackte Optionals verlassen, sollten Sie wahrscheinlich in Betracht ziehen, das Design umzustrukturieren, um die gegenseitigen Abhängigkeiten zu beseitigen.

39voto

Palimondo Punkte 7121

Implizit entpackte Optionale sind ein pragmatischer Kompromiss, um die Arbeit in einer hybriden Umgebung angenehmer zu gestalten, die mit vorhandenen Cocoa-Frameworks und deren Konventionen interoperieren muss, während gleichzeitig eine schrittweise Migration in ein sichereres Programmierparadigma ermöglicht wird - ohne Nullzeiger - durch den Swift-Compiler.

Das Swift-Buch, im Kapitel Die Grundlagen, Abschnitt Implizit entpackte Optionale sagt:

Implizit entpackte Optionale sind nützlich, wenn der Wert einer optionalen Variable unmittelbar nach der Definition der optionalen Variable bestätigt ist und an jeder Stelle danach definitiv vorhanden sein kann. Der Hauptzweck von implizit entpackten Optionalen in Swift liegt beim Initialisieren von Klassen, wie im Abschnitt Unowned-Verweise und implizit entpackte optionale Eigenschaften beschrieben.

Sie können sich ein implizit entpacktes Optional als Erlaubnis vorstellen, das optionale Variable automatisch zu entpacken, wann immer sie verwendet wird. Anstelle eines Ausrufezeichens nach dem Namen der optionalen Variable jedes Mal, wenn Sie sie verwenden, setzen Sie ein Ausrufezeichen nach dem Typ der optionalen Variable, wenn Sie sie deklarieren.

Dies betrifft Anwendungsfälle, bei denen die Nicht-nil-Eigenschaften durch Konventionen bei der Verwendung etabliert sind und nicht während der Klasseninitialisierung vom Compiler erzwungen werden können. Zum Beispiel die UIViewController-Eigenschaften, die aus NIBs oder Storyboards initialisiert werden, wobei die Initialisierung in separate Phasen aufgeteilt ist, aber nach dem viewDidLoad() können Sie davon ausgehen, dass die Eigenschaften im Allgemeinen vorhanden sind. Andernfalls müssten Sie zum Befriedigen des Compilers die erzwungene Entpackung, optionale Bindung oder optionale Verkettung verwenden, um den Hauptzweck des Codes zu verschleiern.

Der oben zitierte Teil des Swift-Buchs bezieht sich auch auf das Kapitel Automatische Referenzzählung:

Es gibt jedoch ein drittes Szenario, in dem beide Eigenschaften immer einen Wert haben sollten und keine der Eigenschaften, sobald die Initialisierung abgeschlossen ist, jemals nil sein sollte. In diesem Szenario ist es nützlich, eine unowned-Eigenschaft in einer Klasse mit einer implizit entpackten optionalen Eigenschaft in der anderen Klasse zu kombinieren.

Dadurch können beide Eigenschaften direkt zugegriffen werden (ohne optionale Entpackung), sobald die Initialisierung abgeschlossen ist, wobei trotzdem ein Verweiszyklus vermieden wird.

Das führt zu den Eigenarten einer Sprache ohne Garbage Collection, daher liegt es an Ihnen als Programmierer, Verweiszylinder zu brechen, und implizit entpackte Optionale sind ein Werkzeug, um diese Eigenart zu verbergen.

Dies umfasst die Frage "Wann sollten implizit entpackte Optionale im Code verwendet werden?". Als Anwendungsprogrammierer werden Sie ihnen hauptsächlich in den Methodensignaturen von in Objective-C geschriebenen Bibliotheken begegnen, die die Möglichkeit haben, optionale Typen auszudrücken.

Von Verwenden von Swift mit Cocoa und Objective-C, Abschnitt Arbeiten mit nil:

Weil Objective-C keine Garantien dafür gibt, dass ein Objekt nicht nil ist, macht Swift alle Klassen in Argumenttypen und Rückgabetypen optional in importierten Objective-C-APIs. Bevor Sie ein Objective-C-Objekt verwenden, sollten Sie überprüfen, ob es nicht fehlt.

In manchen Fällen können Sie sicher sein, dass eine Objective-C-Methode oder Eigenschaft niemals eine nil-Objektreferenz zurückgibt. Um Objekte in diesem speziellen Szenario bequemer zu handhaben, importiert Swift Objekttypen als implizit entpackte Optionale. Implizit entpackte optionale Typen enthalten alle Sicherheitsfunktionen optionaler Typen. Darüber hinaus können Sie auf den Wert direkt zugreifen, ohne auf nil zu prüfen oder ihn selbst zu entpacken. Wenn Sie auf den Wert in diesem Art von optionalem Typ ohne sicheres Entpacken zugreifen, überprüft das implizit entpackte Optional, ob der Wert fehlt. Wenn der Wert fehlt, tritt ein Laufzeitfehler auf. Daher sollten Sie immer ein implizit entpacktes Optional selbst überprüfen und entpacken, es sei denn, Sie sind sicher, dass der Wert nicht fehlen kann.

...und jenseits davon liegen Drachen

19voto

rickster Punkte 121640

Einfache Beispiele in einer Zeile (oder mehreren Zeilen) decken das Verhalten von Optionals nicht sehr gut ab - ja, wenn Sie eine Variable deklarieren und ihr sofort einen Wert zuweisen, hat ein Optional keinen Sinn.

Der beste Fall, den ich bisher gesehen habe, ist die Einrichtung, die nach der Objektinitialisierung erfolgt, gefolgt von einer Verwendung, die "garantiert" ist, dieser Einrichtung zu folgen, z. B. in einem Ansichts-Controller:

class MyViewController: UIViewController {

    var screenSize: CGSize?

    override func viewDidLoad {
        super.viewDidLoad()
        screenSize = view.frame.size
    }

    @IBAction printSize(sender: UIButton) {
        println("Bildschirmgröße: \(screenSize!)")
    }
}

Wir wissen, dass printSize aufgerufen wird, nachdem die Ansicht geladen wurde - es handelt sich um eine Aktion, die mit einer Steuerung innerhalb der Ansicht verbunden ist, und wir haben darauf geachtet, sie nicht anderswo aufzurufen. Daher können wir uns etwas optionale Übeprüfung/Einschließung mit dem ! ersparen. Swift kann diese Garantie nicht erkennen (zumindest bis Apple das Halteproblem löst), also sagen Sie dem Compiler, dass sie existiert.

Dies bricht jedoch die Typsicherheit zu einem gewissen Grad. Jeder Ort, an dem Sie ein implizit ausgepacktes Optional haben, ist ein Ort, an dem Ihre App abstürzen kann, wenn Ihre "Garantie" nicht immer zutrifft, also ist es eine Funktion, die sparsam verwendet werden sollte. Außerdem klingt es nicht gut, wenn man ständig ! benutzt, als würde man schreien, und das mag niemand.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X