Was bedeutet ein Ausrufezeichen in der Swift-Sprache?

518

Das Swift Programming Language-Handbuch enthält das folgende Beispiel:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { println("\(name) is being deinitialized") }
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { println("Apartment #\(number) is being deinitialized") }
}

var john: Person?
var number73: Apartment?

john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)

//From Apple's “The Swift Programming Language” guide (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)

Wenn sie dann die Wohnung der Person zuweisen, verwenden sie ein Ausrufezeichen, um "die Instanz auszupacken":

john!.apartment = number73

Was bedeutet es, "die Instanz auszupacken"? Warum ist es notwendig? Wie unterscheidet es sich von Folgendem:

john.apartment = number73

Ich bin sehr neu in der Swift-Sprache. Ich versuche nur, die Grundlagen zu vermitteln.


UPDATE:
Das große Puzzleteil, das mir fehlte (nicht direkt in den Antworten angegeben - zumindest nicht zum Zeitpunkt des Schreibens), ist das, wenn Sie Folgendes tun:

var john: Person?

das bedeutet NICHT, dass " johnvom Typ ist Personund es Null sein könnte", wie ich ursprünglich dachte. Ich habe das einfach falsch verstanden Personund bin Person?völlig getrennte Typen. Nachdem ich das begriffen hatte , machten all die anderen ?, der !Wahnsinn und die großartigen Antworten unten viel mehr Sinn.

Troy
quelle

Antworten:

530

Was bedeutet es, "die Instanz auszupacken"? Warum ist es notwendig?

Soweit ich trainieren kann (das ist auch für mich sehr neu) ...

Der Begriff "verpackt" impliziert, dass wir uns eine optionale Variable als Geschenk vorstellen sollten, eingewickelt in glänzendes Papier, das (leider!) Leer sein könnte .

Beim "Umschließen" ist der Wert einer optionalen Variablen eine Aufzählung mit zwei möglichen Werten (ähnlich einem Booleschen Wert). Diese Aufzählung beschreibt, ob die Variable einen Wert ( Some(T)) enthält oder nicht ( None).

Wenn es einen Wert gibt, kann dies durch "Auspacken" der Variablen (Erhalten des Tvon Some(T)) erhalten werden.

Wie john!.apartment = number73unterscheidet sich von john.apartment = number73? (Umschrieben)

Wenn Sie den Namen einer optionalen Variablen (z. B. Text johnohne !) schreiben , bezieht sich dies auf die "umschlossene" Aufzählung (Some / None) und nicht auf den Wert selbst (T). Ist johnalso keine Instanz von Personund hat kein apartmentMitglied:

john.apartment
// 'Person?' does not have a member named 'apartment'

Der tatsächliche PersonWert kann auf verschiedene Arten ausgepackt werden:

  • "Forced Unwrapping": john!(gibt den PersonWert an, falls vorhanden, Laufzeitfehler, wenn er Null ist)
  • "optionale Bindung": if let p = john { println(p) }(führt aus, printlnwenn der Wert vorhanden ist)
  • "optionale Verkettung": john?.learnAboutSwift()(führt diese erfundene Methode aus, wenn der Wert vorhanden ist)

Ich denke, Sie wählen eine dieser Möglichkeiten zum Auspacken, je nachdem, was im Fall Null passieren soll und wie wahrscheinlich das ist. Dieses Sprachdesign zwingt dazu, den Null-Fall explizit zu behandeln, was meiner Meinung nach die Sicherheit gegenüber Obj-C verbessert (wo man leicht vergisst, den Null-Fall zu behandeln).

Update :

Das Ausrufezeichen wird auch in der Syntax zum Deklarieren von "Implizit entpackten Optionen" verwendet.

In den bisherigen Beispielen wurde die johnVariable als deklariert var john:Person?und ist optional. Wenn Sie den tatsächlichen Wert dieser Variablen möchten, müssen Sie ihn mit einer der drei oben genannten Methoden auspacken.

Wenn es var john:Person!stattdessen als deklariert würde, wäre die Variable eine implizit nicht verpackte Option (siehe den Abschnitt mit dieser Überschrift in Apples Buch). Diese Art von Variable muss beim Zugriff auf den Wert nicht entpackt werden und johnkann ohne zusätzliche Syntax verwendet werden. Aber Apples Buch sagt:

Implizit entpackte Optionen sollten nicht verwendet werden, wenn die Möglichkeit besteht, dass eine Variable zu einem späteren Zeitpunkt Null wird. Verwenden Sie immer einen normalen optionalen Typ, wenn Sie während der Lebensdauer einer Variablen nach einem Nullwert suchen müssen.

Update 2 :

Der Artikel " Interessante schnelle Funktionen " von Mike Ash gibt einige Motivation für optionale Typen. Ich finde es toll, klar zu schreiben.

Update 3 :

Ein weiterer nützlicher Artikel über die implizit ausgepackte optionale Verwendung des Ausrufezeichens: " Swift and the Last Mile " von Chris Adamson. Der Artikel erklärt, dass dies eine pragmatische Maßnahme von Apple ist, mit der die von den Objective-C-Frameworks verwendeten Typen deklariert werden, die möglicherweise Null enthalten. Das Deklarieren eines Typs als optional (Verwenden ?) oder implizit ausgepackt (Verwenden !) ist "ein Kompromiss zwischen Sicherheit und Komfort". In den Beispielen im Artikel hat Apple beschlossen, die Typen als implizit entpackt zu deklarieren, um den aufrufenden Code bequemer, aber weniger sicher zu machen.

Vielleicht könnte Apple in Zukunft ihre Frameworks durchkämmen, um die Unsicherheit implizit entpackter ("wahrscheinlich nie null") Parameter zu beseitigen und sie durch optionale ("könnte sicherlich keine besonderen [hoffentlich dokumentierten!] Umstände sein") oder Standard-Nicht-Umstände zu ersetzen -optionale ("ist niemals null") Deklarationen, basierend auf dem genauen Verhalten ihres Objective-C-Codes.

Ashley
quelle
Ich bin mir bei dieser Erklärung nicht sicher. Wenn Sie den Code nur ohne! es wird immer noch der tatsächliche Wert zurückgegeben. Vielleicht das ! ist für Geschwindigkeit?
Richard Washington
OK. In den Dokumenten wird also über die Verwendung gesprochen! wenn Sie sicher wissen, dass es ausgepackt werden kann. Sie können den Code jedoch auch ohne ihn ausführen (eine vierte Option für Ihre Liste - implizites Auspacken) UND ohne vorherige Überprüfung. Sie erhalten den Wert oder Null zurück, wenn Null. Aber wenn Sie sicher wissen, dass es nicht Null ist, dann verwenden Sie! ...... aber ich kann immer noch nicht sehen, warum Sie dies tun würden?
Richard Washington
2
Hallo @RichardWashington - Ich habe meiner Antwort ein Update hinzugefügt, das hoffentlich einige davon klarstellt.
Ashley
Update 3 ist genau das, wonach ich suche. Bevor ich es las, dachte ich, Apple lüge, als sie so etwas wie NSURLCredential zurückbrachten! das könnte eigentlich null sein.
Goldener Daumen
Danke für die fantastische Antwort @Ashley. Nachdem ich diese Antwort und einige Beispiele für den schnellen Code von Apple gelesen habe, scheint es mir wirklich, dass es bei diesem Kompromiss zwischen Sicherheit und Produktivität normalerweise am besten ist, auf der sichereren Seite zu sein. Eine bemerkenswerte Ausnahme, die ich gefunden habe, ist, wenn Apple das erzwungene Entpacken für UI-Elemente verwendet. Dies ist sinnvoll, da erstens UI-Elemente normalerweise optional sind, wenn ein Storyboard verwendet wird (da ihnen programmgesteuert kein Wert zugewiesen wird), und zweitens, weil sie fast sind Niemals Null, es sei denn, der enthaltende View-Controller ist Null. In diesem Fall ist diese Diskussion umstritten.
Mihir
127

Folgendes ist meiner Meinung nach der Unterschied:

var john: Person?

Bedeutet, John kann Null sein

john?.apartment = number73

Der Compiler interpretiert diese Zeile wie folgt:

if john != nil {
    john.apartment = number73
}

Während

john!.apartment = number73

Der Compiler interpretiert diese Zeile wie folgt:

john.apartment = number73

Daher mit! wird die if-Anweisung auspacken und schneller ausführen, aber wenn john null ist, tritt ein Laufzeitfehler auf.

Wrap bedeutet hier nicht, dass es sich um Speicher handelt, sondern um Code, in diesem Fall um eine if-Anweisung. Da Apple die Leistung zur Laufzeit genau beachtet, möchten sie Ihnen einen Weg dazu bieten Lassen Sie Ihre App mit der bestmöglichen Leistung laufen.

Aktualisieren:

Zurück zu dieser Antwort nach 4 Jahren, da ich in Stackoverflow den höchsten Ruf davon hatte :) Ich habe die Bedeutung des Auspackens zu dieser Zeit ein wenig falsch verstanden. Jetzt, nach 4 Jahren, glaube ich, dass das Entpacken hier bedeutet, den Code von seiner ursprünglichen kompakten Form zu erweitern. Es bedeutet auch, die Unbestimmtheit um dieses Objekt herum zu entfernen, da wir per Definition nicht sicher sind, ob es null ist oder nicht. Betrachten Sie es genau wie die Antwort von Ashley oben als ein Geschenk, das nichts enthalten könnte. Aber ich denke immer noch, dass das Entpacken Code-Entpacken ist und nicht speicherbasiertes Entpacken wie das Verwenden von Enum.

Amr
quelle
9
Auf meinem Spielplatz wird john.apartment = number73 nicht kompiliert, Sie müssen john angeben? .Apartment = number73
Chuck Pinkert
2
@ChuckPinkert ist richtig, die 4. Zeile sollte zu John bearbeitet werden? .Apartment = number73, nette Antwort aber!
Bruce
john.apartment = number73 gibt einen Fehler aus: Wert des optionalen Typs 'Person?' nicht ausgepackt: wolltest du '!' oder '?'?
Spiderman
4
Das Auspacken hat nichts mit der Leistung zu tun. Die "Nullprüfung" muss noch zur Laufzeit durchgeführt werden. Der einzige Unterschied besteht darin, dass ein Laufzeitfehler ausgegeben wird, falls in der Option kein Wert vorhanden ist.
Madidier
67

TL; DR

Was bedeutet ein Ausrufezeichen in der Swift-Sprache?

Das Ausrufezeichen sagt effektiv: „Ich weiß, dass dieses optionale definitiv einen Wert hat; bitte benutze es. " Dies wird als erzwungenes Auspacken des optionalen Werts bezeichnet:

Beispiel

let possibleString: String? = "An optional string."
print(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."

let assumedString: String! = "An implicitly unwrapped optional string."
print(assumedString)  // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

Quelle: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399

Alex Nolasco
quelle
11
Ihre Antwort ist großartig, weil ich verstehe, was jetzt los ist. Was ich nicht verstehe, ist, warum implizit ausgepackte Optionen existieren. Warum etwas erstellen, das als implizit entpackter optionaler String definiert ist, anstatt als regulärer String-Typ? Ihre Verwendung danach ist die gleiche. Was vermisse ich?
Pachun
Dies scheint nicht mehr wahr zu sein. In einem repl Swift 5, wenn ich es tue , let f: String! = "hello"und dann print(f)ist der Ausgang Optional("hello")statt nur "hello".
Numbermaniac
37

Wenn John eine optionale Var wäre (so deklariert)

var john: Person?

dann wäre es möglich, dass John keinen Wert hat (im ObjC-Sprachgebrauch kein Wert)

Das Ausrufezeichen sagt dem Compiler im Grunde: "Ich weiß, dass dies einen Wert hat, Sie müssen nicht darauf testen." Wenn Sie es nicht verwenden möchten, können Sie es unter bestimmten Bedingungen testen:

if let otherPerson = john {
    otherPerson.apartment = number73
}

Das Innere davon wird nur ausgewertet, wenn John einen Wert hat.

Ben Gottlieb
quelle
4
Vielen Dank für die Antwort. Ich verstehe jetzt, was das Ausrufezeichen sagt. Ich versuche immer noch, meinen Kopf darum zu wickeln, warum ... Sie sagten, es sagt dem Compiler: "Ich weiß, dass dies einen Wert hat, auf den Sie nicht testen müssen es". Was kauft es mir, wenn der Compiler nicht darauf testet? Wird der Compiler ohne das Ausrufezeichen einen Fehler auslösen (ich habe noch keine Umgebung, in der ich Swift testen kann)? Ist der ! 100% notwendig für alle optionalen vars? Wenn ja, warum hat sich Apple damit beschäftigt, anstatt es nur so zu machen, dass es fehlt ! bedeutet "Ich weiß, dass dies einen Wert hat, Sie müssen nicht darauf testen"?
Troy
"Wird der Compiler einen Fehler auslösen?" - Nein, es funktioniert immer noch einwandfrei, wenn der Wert wie erwartet zurückgegeben wird. Ich verstehe das nicht. Ist der ! Nur aus Gründen der Geschwindigkeit, wenn Sie sich sicher sind?
Richard Washington
Tatsächlich wird später in den Dokumenten anhand des folgenden Beispiels über implizit entpackte Optionen gesprochen: “let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.” Aber es funktioniert gut mit no! Hier scheint etwas seltsam.
Richard Washington
3
@ RichardWashington Du bist aus gutem Grund verwirrt! Die Beispiele in den Dokumenten lassen einige Dinge aus und sind irreführend, da sie nur println verwenden. Anscheinend gibt es mindestens einige Fälle, in denen das Auspacken von Optionen nicht erforderlich ist. Eine davon ist die Verwendung von println. Ein weiterer Grund ist die Verwendung der Zeichenfolgeninterpolation. Vielleicht werden Println und String-Interpolation unter der Decke ausgepackt? Vielleicht hat jemand mehr Einblick in diese. Ein Blick auf die Xcode6 Beta-Header-Definitionen hat mir nichts über diese Magie verraten.
mbeaty
Ja, das verstehe ich John?und es Johngibt zwei verschiedene Typen. Eine ist vom Typ Optionale Person und die andere vom Typ Person. Die optionale Person muss ausgepackt werden, bevor Sie die Person herausholen können. Aber wie Sie sagen, scheint dies zumindest unter diesen Umständen zu geschehen, ohne dass Sie tatsächlich etwas tun müssen. Das scheint das zu machen! redundant. Es sei denn, die ! ist IMMER optional, wird jedoch empfohlen, um Fehler bei der Kompilierung abzufangen. Ein bisschen wie das Zuweisen von Vars / Lets zu einem bestimmten Typ kann explizit sein, let John: Person = ...aber auch abgeleitet werden let John = ....
Richard Washington
27

Einige Gesamtperspektiven, um die anderen nützlichen, aber detaillierteren Antworten zu ergänzen:

In Swift wird das Ausrufezeichen in mehreren Kontexten angezeigt:

  • Erzwungenes Auspacken: let name = nameLabel!.text
  • Implizit ausgepackte Optionen: var logo: UIImageView!
  • Zwangsguss: logo.image = thing as! UIImage
  • Unbehandelte Ausnahmen: try! NSJSONSerialization.JSONObjectWithData(data, [])

Jedes von diesen ist ein anderes Sprachkonstrukt mit einer anderen Bedeutung, aber alle haben drei wichtige Dinge gemeinsam:

1. Ausrufezeichen umgehen die Sicherheitsüberprüfungen zur Kompilierungszeit von Swift.

Wenn Sie !in Swift verwenden, sagen Sie im Wesentlichen: "Hey, Compiler, ich weiß, dass Sie denken , dass hier ein Fehler auftreten könnte , aber ich weiß mit absoluter Sicherheit, dass dies niemals der Fall sein wird."

Nicht jeder gültige Code passt in das Feld des Swift-Typsystems zur Kompilierungszeit - oder in die statische Typprüfung einer Sprache. Es gibt Situationen, in denen Sie logisch beweisen können, dass ein Fehler niemals auftreten wird, aber Sie können ihn dem Compiler nicht beweisen . Aus diesem Grund haben die Designer von Swift diese Funktionen in erster Linie hinzugefügt.

Wenn Sie jedoch verwenden !, schließen Sie einen Wiederherstellungspfad für einen Fehler aus, was bedeutet, dass…

2. Ausrufezeichen sind mögliche Abstürze.

Ein Ausrufezeichen lautet außerdem: "Hey Swift, ich bin mir so sicher, dass dieser Fehler niemals auftreten kann, dass es für Sie besser ist, meine gesamte App zum Absturz zu bringen, als für mich, einen Wiederherstellungspfad dafür zu codieren."

Das ist eine gefährliche Behauptung. Es kann das Richtige sein: In geschäftskritischem Code, in dem Sie über die Invarianten Ihres Codes nachgedacht haben, kann es sein, dass eine falsche Ausgabe schlechter ist als ein Absturz.

Wenn ich jedoch !in freier Wildbahn sehe , wird es selten so achtsam verwendet. Stattdessen bedeutet es zu oft: "Dieser Wert war optional und ich habe nicht wirklich darüber nachgedacht, warum es Null sein könnte oder wie ich mit dieser Situation richtig umgehen soll, aber durch Hinzufügen !wurde es kompiliert ... also ist mein Code korrekt, oder?"

Passen Sie auf die Arroganz des Ausrufezeichens auf. Stattdessen…

3. Ausrufezeichen werden am besten sparsam verwendet.

Jedes dieser !Konstrukte hat ein ?Gegenstück, das Sie zwingt, sich mit dem Fehler / Null-Fall zu befassen:

  • Bedingtes Auspacken: if let name = nameLabel?.text { ... }
  • Optionale Optionen: var logo: UIImageView?
  • Bedingte Besetzungen: logo.image = thing as? UIImage
  • Null-bei-Fehler-Ausnahmen: try? NSJSONSerialization.JSONObjectWithData(data, [])

Wenn Sie versucht sind, zu verwenden !, ist es immer gut, sorgfältig zu überlegen, warum Sie nicht ?stattdessen verwenden. Ist ein Absturz Ihres Programms wirklich die beste Option, wenn der !Vorgang fehlschlägt? Warum ist dieser Wert optional / fehlgeschlagen?

Gibt es einen vernünftigen Wiederherstellungspfad, den Ihr Code im Fall Null / Fehler einschlagen könnte? Wenn ja, codieren Sie es.

Wenn es unmöglich Null sein kann, wenn der Fehler niemals auftreten kann, gibt es dann eine vernünftige Möglichkeit, Ihre Logik zu überarbeiten, damit der Compiler das weiß? Wenn ja, mach es; Ihr Code ist weniger fehleranfällig.

Es gibt Zeiten, in denen es keinen vernünftigen Weg gibt, einen Fehler zu behandeln, und das einfache Ignorieren des Fehlers - und damit das Vorgehen mit falschen Daten - wäre schlimmer als ein Absturz. Dies sind die Zeiten, in denen Gewalt ausgepackt wird.

Ich durchsuche regelmäßig meine gesamte Codebasis !und überprüfe jede Verwendung davon. Nur sehr wenige Verwendungen halten einer Prüfung stand. (Zum jetzigen Zeitpunkt enthält das gesamte Siesta-Framework genau zwei Instanzen .)

Das heißt nicht, dass Sie Ihren Code niemals verwenden sollten !- nur, dass Sie ihn achtsam verwenden und ihn niemals zur Standardoption machen sollten.

Paul Cantrell
quelle
func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { if(receiptData == nil) { return false; } return (hasValidTrial(receiptData!) || isNotExpired(receiptData!)) && isNotCancelled(receiptData!) } Angesichts 3. gibt es einen besseren Weg, um es zu schreiben?
Jenson-Button-Event
Sie können sagenfunc isSubscriptionActive(receiptData: NSDictionary?) -> Bool { guard let nonNilReceiptData = receiptData else { return false} return (hasValidTrial(nonNilReceiptData) || isNotExpired(nonNilReceiptData)) && isNotCancelled(nonNilReceiptData) }
rkgibson2
Ausrufezeichen umgehen keine Sicherheitsüberprüfungen. Sie erhalten einen garantierten Absturz, wenn die Option Null ist. Das Optionale ist immer aktiviert. Es ist nur eine sehr brutale Art, eine Sicherheitsüberprüfung durchzuführen.
Gnasher729
@ gnasher729 Wie in der Antwort angegeben, werden die Sicherheitsüberprüfungen zur Kompilierungszeit zugunsten eines sicheren Laufzeitfehlers umgangen .
Paul Cantrell
24

johnist optional var. Kann also einen nilWert enthalten. Um sicherzustellen, dass der Wert nicht gleich Null ist, verwenden Sie a !am Ende des varNamens.

Aus der Dokumentation

„Wenn Sie sicher sind, dass die Option einen Wert enthält, können Sie auf den zugrunde liegenden Wert zugreifen, indem Sie am Ende des Namens der Option ein Ausrufezeichen (!) Einfügen. Das Ausrufezeichen sagt effektiv: „Ich weiß, dass dieses optionale definitiv einen Wert hat; bitte benutze es. "

Eine andere Möglichkeit, den Wert ungleich Null zu überprüfen, ist

    if let j = json {
        // do something with j
    }
Braten
quelle
1
Ja, ich hatte das in der Dokumentation gesehen, aber es scheint immer noch unnötig ... da mir john.apartment = number73auch gesagt wird: "Ich weiß, dass dieses optionale definitiv einen Wert hat; bitte verwenden Sie es." ...
Troy
6
Ja, aber der Punkt ist, dass Swift standardmäßig versucht, Fehler beim Kompilieren abzufangen. Wenn Sie wissen oder zu wissen glauben, dass diese Variable kein Null enthalten kann, können Sie diese Prüfung mithilfe des Ausrufezeichens entfernen.
Lassej
16

Hier sind einige Beispiele:

var name:String = "Hello World"
var word:String?

Wo wordist ein optionaler Wert. bedeutet, dass es einen Wert enthalten kann oder nicht.

word = name 

Hier namehat ein Wert, damit wir ihn zuweisen können

var cow:String = nil
var dog:String!

Wenn doggewaltsam ausgepackt wird, muss es einen Wert enthalten

dog = cow

Die Anwendung stürzt ab, da wir nicht nilausgepackt zugewiesen sind

Ramkumar Chintala
quelle
1
var c:Int = nilwird erhalten: "Nil kann den angegebenen Typ 'int' nicht initialisieren"
Asususer
15

In diesem Fall...

var John: Person!

es bedeutet, dass John anfangs keinen Wert hat, er wird gesetzt und wenn er einmal gesetzt ist, wird er nie wieder null geführt. Daher kann ich der Einfachheit halber die einfachere Syntax für den Zugriff auf eine optionale Variable verwenden, da dies eine "implizit entpackte Option" ist.

Gast
quelle
1
Vielen Dank. Ich hatte auch den folgenden Link gefunden, der dies erklärt. Diese Art von Typ wird als "implizit entpackte Option" bezeichnet. stackoverflow.com/questions/24122601/…
Kaydell
5

Wenn Sie aus einer Sprache der C-Familie stammen, denken Sie "Zeiger auf ein Objekt vom Typ X, bei dem es sich möglicherweise um die Speicheradresse 0 (NULL) handelt", und wenn Sie aus einer dynamisch typisierten Sprache stammen, werden Sie es sein Denken "Objekt, das wahrscheinlich vom Typ X ist, aber vom Typ undefiniert sein könnte". Keines davon ist tatsächlich richtig, obwohl das erste auf Umwegen nahe ist.

Sie sollten sich das so vorstellen, als wäre es ein Objekt wie:

struct Optional<T> {
   var isNil:Boolean
   var realObject:T
}

Wenn Sie Ihren optionalen Wert foo == nildamit testen, dass er wirklich zurückkehrt foo.isNil, und wenn Sie sagen, foo!dass er foo.realObjectmit der Behauptung zurückkehrt, dass foo.isNil == false. Es ist wichtig, dies zu beachten, denn wenn dies footatsächlich Null foo!ist, ist dies ein Laufzeitfehler. Daher möchten Sie normalerweise stattdessen eine bedingte Erlaubnis verwenden, es sei denn, Sie sind sich sehr sicher, dass der Wert nicht Null ist. Diese Art von Trick bedeutet, dass die Sprache stark typisiert werden kann, ohne dass Sie testen müssen, ob die Werte überall gleich Null sind.

In der Praxis verhält es sich nicht wirklich so, weil die Arbeit vom Compiler erledigt wird. Auf hoher Ebene gibt es einen Typ, Foo?der von getrennt Fooist und Funktionen verhindert, die Typ akzeptierenFoo erhalten. Auf einer niedrigen Ebene ist ein optionaler Wert jedoch kein echtes Objekt, da er keine Eigenschaften oder Methoden hat. Es ist wahrscheinlich, dass es sich tatsächlich um einen Zeiger handelt, der beim Force-Entpacken NULL (0) mit dem entsprechenden Test verwenden kann.

Es gibt eine andere Situation, in der ein Ausrufezeichen auf einem Typ angezeigt wird, wie in:

func foo(bar: String!) {
    print(bar)
}

Dies entspricht in etwa der Annahme einer Option mit erzwungenem Auspacken, dh:

func foo(bar: String?) {
    print(bar!)
}

Sie können dies verwenden, um eine Methode zu haben, die technisch einen optionalen Wert akzeptiert, aber einen Laufzeitfehler aufweist, wenn er Null ist. In der aktuellen Version von Swift wird die is-not-nil-Behauptung anscheinend umgangen, sodass stattdessen ein Fehler auf niedriger Ebene auftritt. Im Allgemeinen keine gute Idee, aber es kann nützlich sein, wenn Code aus einer anderen Sprache konvertiert wird.

Jim Driscoll
quelle
3

Wenn Sie mit C # vertraut sind, ähnelt dies nullbaren Typen, die ebenfalls mit einem Fragezeichen deklariert werden:

Person? thisPerson;

Das Ausrufezeichen entspricht in diesem Fall dem Zugriff auf die .Value-Eigenschaft des nullbaren Typs wie folgt:

thisPerson.Value
Abdurrahman
quelle
2

In Ziel C waren Variablen ohne Wert gleich 'nil' (es war auch möglich, 'nil'-Werte wie 0 und false zu verwenden), daher war es möglich, Variablen in bedingten Anweisungen zu verwenden (Variablen mit Werten sind gleich' TRUE ' 'und diejenigen ohne Werte waren gleich' FALSE ').

Swift bietet Typensicherheit durch Bereitstellung eines optionalen Werts. Das heißt, es verhindert, dass Fehler entstehen, die Variablen unterschiedlichen Typs zuweisen.

In Swift können also nur Boolesche Werte für bedingte Anweisungen bereitgestellt werden.

var hw = "Hello World"

Obwohl 'hw' eine Zeichenfolge ist, kann sie hier nicht in einer if-Anweisung wie in Ziel C verwendet werden.

//This is an error

if hw

 {..}

Dafür muss es erstellt werden als,

var nhw : String? = "Hello World"

//This is correct

if nhw

 {..}
Gokul
quelle
2

Das ! Am Ende eines Objekts steht, dass das Objekt optional ist und ausgepackt werden soll, wenn es sonst eine Null zurückgeben kann. Dies wird häufig verwendet, um Fehler abzufangen, die das Programm sonst zum Absturz bringen würden.

Cheborneck
quelle
2

Kurz (!): Nachdem Sie eine Variable deklariert haben und sicher sind, dass die Variable einen Wert enthält.

let assumedString: String! = "Some message..."
let implicitString: String = assumedString

sonst müssten Sie dies bei jedem nach Übergabe des Wertes tun ...

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
En Hui Lim
quelle
1

John ist eine optionale Person, was bedeutet, dass sie einen Wert enthalten oder Null sein kann.

john.apartment = number73

wird verwendet, wenn John nicht optional ist. Da John niemals Null ist, können wir sicher sein, dass die Wohnung nicht mit Null bewertet wird. Während

john!.apartment = number73

verspricht dem Compiler, dass John nicht gleich Null ist, packt dann die Option aus, um Johns Wert zu erhalten, und greift auf Johns Apartment-Eigentum zu. Verwenden Sie dies, wenn Sie wissen, dass John nicht Null ist. Wenn Sie dies mit null optional aufrufen, wird ein Laufzeitfehler angezeigt.

Die Dokumentation enthält ein schönes Beispiel für die Verwendung, wobei convertNumber optional ist.

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}
Connor
quelle
Wollen Sie damit sagen, dass wenn John Null ist und ich gehe john.apartment = number73, das NICHT zu einem Fehler führt, es sei denn, ich setze ein '!' nach john?
Troy
Wenn John optional ist, müssen Sie das Ausrufezeichen verwenden, um auf seine Eigenschaften zuzugreifen, da der Compiler wissen muss, dass es nicht Null ist. Wenn dies nicht optional ist, können Sie das Ausrufezeichen nicht verwenden.
Connor
1
Wenn ich ein Ausrufezeichen für ALLE optionalen Variablen verwenden muss, warum hat sich Apple dann überhaupt darum gekümmert, anstatt es so zu gestalten, dass das Fehlen eines Ausrufezeichens dasselbe bedeutet? Es scheint unnötig aufzublähen ... Oder gibt es einen Fall, in dem es sinnvoll ist, KEIN Ausrufezeichen mit einer optionalen Variable zu verwenden?
Troy
Sie verwenden das Ausrufezeichen, wenn das optionale Zeichen nicht Null sein soll. Wie im Fall des Zugriffs auf Eigenschaften von John. du könntest so etwas wie var jack machen: Person? = john wobei jack auch ein optionaler oder var jack ist: Person = john! wo Jack eine Person ist und nicht Null ist
Connor
Richtig, aber im ursprünglichen Beispiel sagt die Tatsache, dass ich das johnoptionale dereferenziere , dem Compiler nicht, dass es nicht Null sein muss? ... Und in Ihrem var jack: Person = john!Beispiel fehlt nicht das? Nach Person dem Compiler mitteilen, dass Sie johnnicht null sein müssen? Insgesamt !scheint das nur irgendwie überflüssig zu sein ... Es fühlt sich immer noch so an, als würde mir etwas im "Warum" von !...
Troy
1

Um es einfach auszudrücken: Ausrufezeichen bedeuten, dass eine Option ausgepackt wird. Eine Option ist eine Variable, die einen Wert haben kann oder nicht. Sie können also mithilfe einer if let-Anweisung wie hier gezeigt überprüfen, ob die Variable leer ist , und dann das Entpacken erzwingen. Wenn Sie das Entpacken einer leeren Option erzwingen, stürzt Ihr Programm ab. Seien Sie also vorsichtig! Optionale Optionen werden deklariert, indem am Ende einer expliziten Zuweisung zu einer Variablen ein Fragezeichen gesetzt wird. Ich könnte beispielsweise Folgendes schreiben:

var optionalExample: String?

Diese Variable hat keinen Wert. Wenn ich es auspacken würde, würde das Programm abstürzen und Xcode würde Ihnen sagen, dass Sie versucht haben, ein optionales mit dem Wert nil auszupacken.

Hoffe das hat geholfen.

Schwefel
quelle
1

IN EINFACHEN WORTEN

USING Das Ausrufezeichen gibt an, dass die Variable aus einem Wert ungleich Null bestehen muss (niemals Null).

Maninderjit Singh
quelle
1

Die gesamte Geschichte beginnt mit einer schnellen Funktion, die als optionale Vars bezeichnet wird. Dies sind die Variablen, die einen Wert haben können oder keinen Wert haben können. Im Allgemeinen erlaubt uns swift nicht, eine Variable zu verwenden, die nicht initialisiert ist, da dies zu Abstürzen oder unerwarteten Gründen führen kann und auch einen Platzhalter für Hintertüren dient. Um eine Variable zu deklarieren, deren Wert zunächst nicht bestimmt wurde, verwenden wir ein '?'. Wenn eine solche Variable deklariert wird, um sie als Teil eines Ausdrucks zu verwenden, muss sie vor der Verwendung entpackt werden. Das Entpacken ist eine Operation, durch die der Wert einer Variablen ermittelt wird. Dies gilt für Objekte. Wenn Sie versuchen, sie zu verwenden, wird beim Kompilieren ein Fehler beim Kompilieren angezeigt. Um eine Variable zu entpacken, die eine optionale Variable ist, das Ausrufezeichen "!" wird genutzt.

Es gibt Zeiten, in denen Sie wissen, dass solchen optionalen Variablen beispielsweise vom System oder Ihrem eigenen Programm Werte zugewiesen werden, aber einige Zeit später, z. B. UI-Outlets, in einer solchen Situation, anstatt eine optionale Variable mit einem Fragezeichen "?" Zu deklarieren. wir gebrauchen "!".

Somit weiß das System, dass diese Variable, die mit "!" ist momentan optional und hat keinen Wert, wird aber später in seiner Lebensdauer einen Wert erhalten.

Das Ausrufezeichen enthält also zwei verschiedene Verwendungen: 1. Eine Variable deklarieren, die optional ist und definitiv später einen Wert erhält. 2. Eine optionale Variable auspacken, bevor sie in einem Ausdruck verwendet wird.

Die obigen Beschreibungen vermeiden hoffentlich zu viel technisches Zeug.

Vishal Dharankar
quelle
1

Wenn Sie es als optional verwenden, packt es das optionale aus und prüft, ob etwas vorhanden ist. Wenn Sie es in einer if-else-Anweisung verwenden, ist dies Code für NOT. Zum Beispiel,

if (myNumber != 3){
 // if myNumber is NOT 3 do whatever is inside these brackets.
)
Andy Lebowitz
quelle
1

Eine optionale Variable kann einen Wert enthalten oder nicht

Fall 1: var myVar:String? = "Something"

Fall 2: var myVar:String? = nil

Wenn Sie jetzt myVar! fragen, weisen Sie den Compiler an, einen Wert zurückzugeben, falls 1 zurückgegeben wird "Something"

in Fall 2 wird es abstürzen.

Bedeutung! mark zwingt den Compiler, einen Wert zurückzugeben, auch wenn dieser nicht vorhanden ist. Deshalb der Name Force Unwrapping .

Ashish Pisey
quelle
@ Moritz. Ich weiß nicht, ist es so? Kann ich nicht antworten, wenn eine Frage bereits beantwortet wurde? Verursache ich ein Problem oder stimmt etwas in meiner Antwort nicht? Ich verstehe nicht Warum stimmen Sie es ab, weil Sie es richtig beantwortet haben? Ich habe versucht zu antworten, basierend auf meinem Verständnis des Konzepts. Ich denke, einige Leute werden es hilfreich finden, um es klar und einfach zu erklären.
Ashish Pisey
@Moritz Nun das ist konstruktiv. Sie hätten mir diese Korrektur an erster Stelle sagen sollen, wenn dies der Grund gewesen wäre. Danke für die Rückmeldung. Ich habe es korrigiert.
Ashish Pisey
0
Simple the Optional variable allows nil to be stored.

var str : String? = nil

str = "Data"

To convert Optional to the Specific DataType, We unwrap the variable using the keyword "!"

func get(message : String){
   return
}

get(message : str!)  // Unwapped to pass as String
Jeff Ayan
quelle
1
Bitte schreiben Sie keine Antworten, die nichts hinzufügen, was noch nicht durch vorherige Antworten abgedeckt wurde.
Dalija Prasnikar
-1

FRAG DICH SELBST

  • Hat der Typ person?ein apartmentMitglied / eine Eigenschaft? ODER
  • Hat der Typ personein apartmentMitglied / eine Eigenschaft?

Wenn Sie diese Frage nicht beantworten können, lesen Sie weiter:

Um dies zu verstehen, benötigen Sie möglicherweise ein grundlegendes Verständnis für Generika . Siehe hier . Viele Dinge in Swift werden mit Generics geschrieben. Optionale enthalten

Der folgende Code wurde in diesem Stanford-Video zur Verfügung gestellt . Sehr zu empfehlen, die ersten 5 Minuten zu sehen

Eine Option ist eine Aufzählung mit nur 2 Fällen

enum Optional<T>{
    case None
    case Some(T)
}

let x: String? = nil //actually means:

let x = Optional<String>.None

let x :String? = "hello" //actually means:

let x = Optional<String>.Some("hello")

var y = x! // actually means:

switch x {
case .Some(let value): y = value
case .None: // Raise an exception
}

Optionale Bindung:

let x:String? = something
if let y = x {
    // do something with y
}
//Actually means:

switch x{
case .Some(let y): print)(y) // or whatever else you like using 
case .None: break
}

wenn du sagst var john: Person?meinst du eigentlich so:

enum Optional<Person>{
case .None
case .Some(Person)
}

Hat die obige Aufzählung eine Eigenschaft namens apartment? Siehst du es irgendwo Es ist überhaupt nicht da! Wenn Sie es jedoch auspacken, dh tun person!, können Sie ... was es unter der Haube tut, ist:Optional<Person>.Some(Person(name: "John Appleseed"))


Hättest du statt definiert var john: Person: var john: Person?dann hättest du das nicht mehr gebraucht !, weil es Personselbst ein Mitglied von hatapartment


Eine zukünftige Diskussion darüber, warum !das Auspacken manchmal nicht empfohlen wird, finden Sie in diesen Fragen und Antworten

Honig
quelle
2
Die Verwendung der oben genannten Folien stellt möglicherweise eine Urheberrechtsverletzung dar: "... Die Stanford University behält das Urheberrecht an allen Inhalten in unserer iTunes U-Sammlung." , von http://itunes.stanford.edu . Wahrscheinlich ist es besser, den Inhalt, den Sie aus dem Kurs gelernt haben, in Ihren eigenen Worten zu beantworten, um diese Frage zu beantworten (und anstatt die Folien zu verwenden, zitieren Sie relevante Teile davon natürlich in Codeform, natürlich mit Referenzen).
dfri
2
Ihr Argument ist argumentum ad populum . Wie auch immer, ich glaube nicht, dass Stanford hierher kommen und DMCA-Rechte durchsetzen wird , aber es ist immer besser, wenn Ihre Antworten in Ihren eigenen Worten auf offiziellen / gültigen Quellen basieren und nicht in Form des direkten Kopierens dieser Quellen wie oben; Besonders wenn wir über urheberrechtlich geschützte Folien sprechen: Auch hier ist es besser, den Inhalt der Folien in Codeblöcken zu zitieren (Bilder von Code sind hier bei SO im Allgemeinen ein Nein-Nein!).
dfri
1
Der Meta-Beitrag, auf den ich verlinkt habe, beschreibt nur die Vorgehensweise von SO: Ein Beitrag wie dieser wird natürlich nicht entfernt, wenn ein zufälliger Benutzer ihn als Urheberrechtsverletzung meldet: nur wenn z. B. ein Stanford-Vertreter. waren hier und da die Post kommen würde entfernt werden , so brauchen sie zu erzwingen. Daher schrieb ich: " Ich glaube nicht, dass Stanford hierher kommen und DMCA-Rechte durchsetzen wird, ..." . Mein Punkt oben ist, dass ich glaube, dass dies möglicherweise eine Urheberrechtsverletzung ist (was ich für falsch halte), aber natürlich wird niemand dies jemals durchsetzen. ...
dfri
1
Abgesehen davon wird aus mehreren Gründen davon abgeraten , Fragen oder Antworten zu veröffentlichen, die Bilder von Code enthalten . Um es zusammenzufassen: Ich habe versucht, mit diesen Kommentaren darauf hinzuweisen, dass ich glaube, dass diese Antwort in ihrer aktuellen Form aus zwei Hauptgründen (urheberrechtlich geschützte Folien, Bild des Codes) nicht ganz so gut ist, wie sie sein könnte. Natürlich wird niemand, auch ich, etwas dagegen unternehmen. Ich verweise nur auf unsere, um später darauf zurückgreifen zu können (oder ermutige Sie möglicherweise, diesen Beitrag stattdessen mit einem Zitat durch Codeblöcke zu bearbeiten). Einverstanden, iTunes U! :)
dfri
2
@dfri entfernte Bilder
Honey