it-swarm.com.de

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

Warum sollten Sie ein "Implizit entpacktes Optional" erstellen, anstatt nur eine reguläre Variable oder Konstante zu erstellen? Wenn Sie wissen, dass es erfolgreich entpackt werden kann, warum sollten Sie dann zunächst ein optionales Element erstellen? Zum Beispiel, warum ist das:

let someString: String! = "this is the string"

wird nützlicher sein als:

let someString: String = "this is the string"

Wenn "optionals anzeigen, dass eine Konstante oder Variable 'keinen Wert' haben darf", aber "manchmal aus der Programmstruktur hervorgeht, dass eine optionale Variable immer einen Wert hat, nachdem dieser Wert zum ersten Mal festgelegt wurde", woran liegt das? machen es zu einem optionalen in erster Linie? Wenn Sie wissen, dass ein optionaler Wert immer einen Wert hat, ist er dann nicht optional?

479
Johnston

Stellen Sie sich den Fall eines Objekts vor, das während der Erstellung und Konfiguration möglicherweise keine Eigenschaften aufweist, aber danach unveränderlich und nicht null ist (NSImage wird häufig auf diese Weise behandelt, obwohl es in diesem Fall manchmal immer noch nützlich ist, zu mutieren). Implizit ausgepackte Optionen würden den Code mit relativ geringem Sicherheitsverlust weitgehend bereinigen (solange die eine Garantie besteht, wäre sie sicher).

(Bearbeiten) Um es klar zu sagen: Regelmäßige Optionen sind fast immer vorzuziehen.

126
Catfish_Man

Bevor ich die Anwendungsfälle für implizit ausgepackte Optionen beschreiben kann, sollten Sie bereits verstehen, welche Optionen und implizit ausgepackten Optionen in Swift enthalten sind. Wenn Sie dies nicht tun, empfehle ich Ihnen, zuerst meinen Artikel über optionals zu lesen

Wann wird ein implizit ausgepacktes optionales Element verwendet?

Es gibt zwei Hauptgründe, aus denen ein implizit ausgepacktes optionales Element erstellt wird. Alles hat damit zu tun, eine Variable zu definieren, auf die beim nil nie zugegriffen wird, da der Swift) - Compiler Sie sonst immer zwingt, ein Optional explizit zu entpacken.

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

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

Die Verwendung einer optionalen Variablen umgeht dieses Problem, da eine optionale Variable automatisch mit nil initialisiert wird und der Wert, den sie möglicherweise enthält, weiterhin unveränderlich ist. Es kann jedoch mühsam sein, eine Variable, von der Sie sicher wissen, dass sie nicht null ist, ständig auszupacken. Implizit ausgepackte Optionen bieten die gleichen Vorteile wie eine Option mit dem zusätzlichen Vorteil, dass sie nicht überall explizit ausgepackt werden müssen.

Ein gutes 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 der Schaltfläche erst berechnen, wenn die Ansicht geladen ist. Sie wissen jedoch, dass awakeFromNib vor allen anderen Methoden in der Ansicht aufgerufen wird (außer der Initialisierung). Anstatt zu erzwingen, dass der Wert in Ihrer Klasse explizit sinnlos umbrochen wird, können Sie ihn als implizit umbrochenes optionales Element deklarieren.

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

Dies sollte äußerst selten vorkommen, aber wenn Ihre App beim Zugriff nicht weiter ausgeführt werden kann, wenn eine Variable nil ist, wäre es Zeitverschwendung, sie auf nil zu testen. Normalerweise verwenden Sie ein assert, wenn Sie eine Bedingung haben, die unbedingt erfüllt sein muss, damit Ihre App weiter ausgeführt werden kann. In einem implizit entpackten optionalen Element ist eine Zusicherung für null direkt integriert. Selbst dann ist es oft gut, das optionale zu entpacken und eine aussagekräftigere Aussage zu verwenden, wenn es null ist.

Wenn Sie ein implizit ausgepacktes optionales Element nicht verwenden möchten

1. Lazily Calculated Member Variables

Manchmal haben Sie eine Mitgliedsvariable, die niemals null sein sollte, die jedoch während der Initialisierung nicht auf den richtigen Wert gesetzt werden kann. Eine Lösung besteht darin, ein implizit entpacktes optionales Element zu verwenden. Eine bessere Möglichkeit besteht darin, eine verzögerte Variable zu verwenden:

class FileSystemItem {
}

class Directory : FileSystemItem {
    lazy var contents : [FileSystemItem] = {
        var loadedContents = [FileSystemItem]()
        // load contents and append to loadedContents
        return loadedContents
    }()
}

Jetzt wird die Mitgliedsvariable contents erst beim ersten Zugriff initialisiert. Dies gibt der Klasse die Möglichkeit, in den richtigen Zustand zu gelangen, bevor der Anfangswert berechnet wird.

Hinweis: Dies scheint Nummer 1 von oben zu widersprechen. Es ist jedoch ein wichtiger Unterschied zu machen. Das buttonOriginalWidth oben muss während viewDidLoad festgelegt werden, um zu verhindern, dass die Breite der Schaltflächen geändert wird, bevor auf die Eigenschaft zugegriffen wird.

2. Überall sonst

Implizit ausgepackte Optionen sollten größtenteils vermieden werden, da bei falscher Verwendung die gesamte App abstürzt, wenn auf sie zugegriffen wird, während nil. Wenn Sie sich nie sicher sind, ob eine Variable null sein kann, verwenden Sie standardmäßig immer ein normales Optional. Das Auspacken einer Variablen, die niemals nil ist, tut sicherlich nicht sehr weh.

453
drewag

Implizit ausgepackte Optionen sind nützlich, um eine Eigenschaft als nicht optional darzustellen, wenn sie tatsächlich unter der Decke optional sein muss. Dies ist häufig erforderlich, um zwei verwandte Objekte miteinander zu verknüpfen, von denen jedes einen Bezug zum anderen benötigt. Es ist sinnvoll, wenn keine Referenz tatsächlich optional ist, aber eine davon null sein muss, während das Paar initialisiert wird.

Zum Beispiel:

// These classes are buddies that never go anywhere without each other
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:"Big A")
println(a.myBuddyB.name)   // prints "Big A's buddy B"

Jede B -Instanz sollte immer eine gültige myBuddyA -Referenz haben, damit der Benutzer sie nicht als optional behandelt, sondern sie muss optional sein, damit wir eine B, bevor wir uns auf ein A beziehen können.

JEDOCH! Diese Art der gegenseitigen Referenzanforderung ist oft ein Hinweis auf eine enge Kopplung und ein schlechtes Design. Wenn Sie sich auf implizit entpackte Optionen verlassen, sollten Sie wahrscheinlich eine Umgestaltung in Betracht ziehen, um die gegenseitigen Abhängigkeiten zu beseitigen.

55
n8gray

Implizit unverpackte Optionen sind ein pragmatischer Kompromiss, um die Arbeit in einer hybriden Umgebung, die mit vorhandenen Cocoa-Frameworks und deren Konventionen zusammenarbeiten muss, angenehmer zu gestalten und gleichzeitig eine schrittweise Migration in ein sichereres Programmierparadigma - ohne Nullzeiger - zu ermöglichen, das vom Swift Compiler.

Swift book, in The Basics Kapitel, Abschnitt Implizit ausgepackte Optionen sagt:

Implizit ausgepackte Optionen sind nützlich, wenn bestätigt wird, dass der Wert einer Option unmittelbar nach der erstmaligen Definition der Option vorhanden ist, und wenn davon ausgegangen werden kann, dass er zu jedem späteren Zeitpunkt vorhanden ist. Die primäre Verwendung von implizit entpackten Optionen in Swift) erfolgt während der Klasseninitialisierung, wie in Nicht im Besitz befindliche Verweise und implizit entpackte optionale Eigenschaften beschrieben.

Sie können sich ein implizit ausgepacktes optionales Element so vorstellen, dass es die Erlaubnis gibt, dass das optionale Element bei jeder Verwendung automatisch ausgepackt wird. Anstatt bei jeder Verwendung ein Ausrufezeichen nach dem Namen der Option einzufügen, setzen Sie bei der Deklaration ein Ausrufezeichen nach dem Typ der Option.

Dies ist auf Anwendungsfälle zurückzuführen, in denen die Nicht-nil- ness von Eigenschaften über eine Verwendungskonvention festgelegt wird und von nicht erzwungen werden kann Compiler während der Klasseninitialisierung. Zum Beispiel die Eigenschaften UIViewController, die über NIBs oder Storyboards initialisiert werden, wobei die Initialisierung in separate Phasen unterteilt ist. Nach der Funktion viewDidLoad() können Sie jedoch davon ausgehen, dass Eigenschaften im Allgemeinen vorhanden sind. Andernfalls mussten Sie erzwungenes Auspacken , optionales Binden oder optionales Verketten verwenden, um den Hauptzweck zu verschleiern des Codes.

Der obige Teil aus dem Buch Swift bezieht sich auch auf das Kapitel Automatic Reference Counting :

Es gibt jedoch ein drittes Szenario, in dem beide Eigenschaften immer einen Wert haben sollten und keine Eigenschaft jemals nil sein sollte, sobald die Initialisierung abgeschlossen ist. In diesem Szenario ist es hilfreich, eine nicht in Besitz genommene Eigenschaft für eine Klasse mit einer implizit nicht umschlossenen optionalen Eigenschaft für die andere Klasse zu kombinieren.

Auf diese Weise kann nach Abschluss der Initialisierung direkt auf beide Eigenschaften zugegriffen werden (ohne optionales Entpacken), ohne dass ein Referenzzyklus erforderlich ist.

Dies hängt mit den Macken zusammen, dass Sie keine überflüssig gesammelte Sprache sind. Daher liegt es an Ihnen als Programmierer, die Retain-Zyklen zu durchbrechen, und implizit ausgepackte Optionen sind ein Werkzeug, um diese Macken zu verbergen.

Dies deckt die Frage ab, wann implizit ausgepackte Optionen in Ihrem Code verwendet werden sollen. Als Anwendungsentwickler begegnen Sie diesen meist in Methodensignaturen von Bibliotheken, die in Objective-C geschrieben wurden und die keine optionalen Typen ausdrücken können.

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

Da Objective-C nicht garantiert, dass ein Objekt nicht null ist, werden Swift in importierten Objective-C-APIs alle Klassen in Argumenttypen und Rückgabetypen optional. Bevor Sie ein Objective-C-API verwenden, C-Objekt sollten Sie überprüfen, um sicherzustellen, dass es nicht fehlt.

In einigen Fällen können Sie absolut sicher sein, dass eine Objective-C-Methode oder -Eigenschaft niemals einen nil -Objektverweis zurückgibt. Um die Arbeit mit Objekten in diesem speziellen Szenario zu vereinfachen, importiert Swift) Objekttypen als implizit ausgepackte Optionsfelder . Implizit Ausgepackte optionale Typen enthalten alle Sicherheitsfunktionen der optionalen Typen. Außerdem können Sie auf den Wert direkt zugreifen, ohne nach nil zu suchen, oder ihn selbst auspacken. Wenn Sie auf den Wert in einem solchen optionalen Typ zugreifen, ohne ihn sicher auspacken zu müssen Wenn der Wert fehlt, tritt ein Laufzeitfehler auf. Daher sollten Sie einen implizit ausgepackten optionalen Wert immer selbst überprüfen und auspacken, es sei denn, Sie sind sicher, dass der Wert nicht vorhanden ist fehlt.

... und dahinter lag dragons

37
Palimondo

Einzeilige (oder mehrzeilige) einfache Beispiele decken das Verhalten von Optionen nicht sehr gut ab. Ja, wenn Sie eine Variable deklarieren und sie sofort mit einem Wert versehen, hat eine Option keinen Sinn.

Der beste Fall, den ich bisher gesehen habe, ist die Einrichtung, die nach der Objektinitialisierung erfolgt, gefolgt von der Verwendung, die "garantiert" dieser Einrichtung folgt, z. in einem View Controller:

class MyViewController: UIViewController {

    var screenSize: CGSize?

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

    @IBAction printSize(sender: UIButton) {
        println("Screen size: \(screenSize!)")
    }
}

Wir wissen, dass printSize aufgerufen wird, nachdem die Ansicht geladen wurde - es ist eine Aktionsmethode, die mit einem Steuerelement in dieser Ansicht verknüpft ist, und wir haben sichergestellt, dass sie nicht anders aufgerufen wird. So können wir uns mit dem ! Ein optionales Prüfen/Binden ersparen. Swift kann diese Garantie nicht erkennen (zumindest bis Apple löst das Problem des Anhaltens)), so dass Sie dem Compiler mitteilen, dass es existiert.

Dies bricht jedoch die Typensicherheit bis zu einem gewissen Grad. An jedem Ort, an dem Sie ein implizit ausgepacktes optionales Feature haben, kann Ihre App abstürzen, wenn Ihre "Garantie" nicht immer gültig ist. Daher sollten Sie diese Funktion sparsam einsetzen. Außerdem klingt die Verwendung von ! Die ganze Zeit so, als würden Sie schreien, und niemand mag das.

19
rickster

Apple gibt ein großartiges Beispiel in Die Swift Programmiersprache -> Automatische Referenzzählung -> Auflösen starker Referenzzyklen zwischen Klasseninstanzen -> Unbesessene Referenzen und implizit Entpackte optionale Eigenschaften

class Country {
    let name: String
    var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var)
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

Der Initialisierer für City wird innerhalb des Initialisierers für Country aufgerufen. Der Initialisierer für Country kann jedoch nicht self an den City -Initialisierer übergeben, bis eine neue Country -Instanz vollständig initialisiert ist, wie in Zwei) beschrieben -Phase Initialisierung .

Um diese Anforderung zu erfüllen, deklarieren Sie die Eigenschaft capitalCity von Country als implizit entpackte optionale Eigenschaft.

15
fujianjin6471

Wenn Sie sicher sind, wird ein Wert von einem optionalen Wert anstelle von nil zurückgegeben. Implizit ausgepackte Optionswerte werden verwendet, um diese Werte direkt von Optionswerten abzufangen und nicht optionals können nicht.

//Optional string with a value
let optionalString: String? = "This is an optional String"

//Declaration of an Implicitly Unwrapped Optional String
let implicitlyUnwrappedOptionalString: String!

//Declaration of a non Optional String
let nonOptionalString: String

//Here you can catch the value of an optional
implicitlyUnwrappedOptionalString = optionalString

//Here you can't catch the value of an optional and this will cause an error
nonOptionalString = optionalString

Das ist also der Unterschied zwischen der Verwendung von

let someString : String! und let someString : String

3
enadun

Das Grundprinzip impliziter Optionen lässt sich leichter erklären, indem zunächst das Grundprinzip für das erzwungene Auspacken betrachtet wird.

Erzwungenes Auspacken eines optionalen (impliziten oder nicht impliziten) Befehls mit dem Befehl! Operator bedeutet, dass Sie sicher sind, dass Ihr Code keine Fehler enthält und der optionale Code bereits einen Wert hat, in dem er entpackt wird. Ohne das ! Betreiber, würden Sie wahrscheinlich nur mit einer optionalen Bindung behaupten:

 if let value = optionalWhichTotallyHasAValue {
     println("\(value)")
 } else {
     assert(false)
 }

das ist nicht so schön wie

println("\(value!)")

Mit impliziten Optionen können Sie nun ausdrücken, dass Sie ein optionales Element haben, von dem Sie erwarten, dass es immer in allen möglichen Flüssen einen Wert aufweist, wenn es ausgepackt ist. Es geht also noch einen Schritt weiter, um Ihnen zu helfen - indem Sie das Erfordernis des Schreibens des! jedes Mal zu entpacken und sicherzustellen, dass die Laufzeit immer noch fehlerhaft ist, falls Ihre Annahmen über den Ablauf falsch sind.

3
Danra

Ich denke, Optional ist ein schlechter Name für dieses Konstrukt, das viele Anfänger verwirrt.

Andere Sprachen (z. B. Kotlin und C #) verwenden den Begriff Nullable, was das Verständnis erheblich erleichtert.

Nullable bedeutet, dass Sie einer Variablen dieses Typs einen Nullwert zuweisen können. Also, wenn es Nullable<SomeClassType>, Sie können ihm Nullen zuweisen, wenn es nur SomeClassType ist, können Sie es nicht. So funktioniert Swift.

Warum sie benutzen? Manchmal muss man Nullen haben, deshalb. Wenn Sie beispielsweise wissen, dass Sie ein Feld in einer Klasse haben möchten, es aber beim Erstellen einer Instanz dieser Klasse keinem Element zuweisen können, werden Sie dies später tun. Ich werde keine Beispiele nennen, weil die Leute sie hier bereits zur Verfügung gestellt haben. Ich schreibe dies nur auf, um meine 2 Cent zu geben.

Übrigens, ich schlage vor, Sie schauen sich an, wie dies in anderen Sprachen wie Kotlin und C # funktioniert.

Hier ist ein Link, der diese Funktion in Kotlin erklärt: https://kotlinlang.org/docs/reference/null-safety.html

Andere Sprachen wie Java und Scala haben Optionals, aber sie funktionieren anders als Optionals in Swift, weil = Java und Scala-Typen können standardmäßig alle auf null gesetzt werden.

Alles in allem denke ich, dass diese Funktion in Swift Nullable hätte heißen sollen und nicht Optional...

0
hey_you