Wat is het verschil tussen String? en String! (twee manieren om een ​​optionele variabele aan te maken)?

In The Swift Programming Language(book of Apple) heb ik gelezen dat je op 2 manieren optionele variabelen kunt maken: met een vraagteken (?) of met een uitroepteken (!) .

Het verschil is dat wanneer je de waarde van een optional krijgt met (?) je elke keer dat je de waarde wilt een uitroepteken moet gebruiken:

var str: String? = "Question mark?"
println(str!) // Exclamation mark needed
str = nil    

Hoewel je het met een (!) kunt krijgen zonder een achtervoegsel:

var str: String! = "Exclamation mark!"
println(str) // No suffix needed
str = nil

Wat is het verschil en waarom zijn er 2 manieren als er helemaal geen verschil is?


Antwoord 1, autoriteit 100%

Het echte voordeel van het gebruik van impliciet uitgepakte optionele opties (aangegeven met de !) is gerelateerd aan de initialisatie van klassen wanneer twee klassen naar elkaar wijzen en je een sterke-referentiecyclus moet vermijden. Bijvoorbeeld:

Klasse A <-> Klasse B

De init-routine van klasse A moet klasse B creëren (en bezitten), en B heeft een zwakke verwijzing naar A nodig:

class A {
    let instanceOfB: B!
    init() {
        self.instanceOfB = B(instanceOfA: self)
    }
}
class B {
    unowned let instanceOfA: A
    init(instanceOfA: A) {
        self.instanceOfA = instanceOfA
    }
}

Nu,

  • Klasse B heeft een verwijzing naar klasse A nodig om te worden geïnitialiseerd.
  • Klasse A kan selfpas doorgeven aan de initialisator van klasse B als deze volledig is geïnitialiseerd.
  • Om Klasse A als geïnitialiseerd te beschouwen voordat Klasse B wordt gemaakt, moet de eigenschap instanceOfBdaarom optioneel zijn.

Als A eenmaal is gemaakt, zou het echter vervelend zijn om instanceOfB te moeten openen met instanceOfB! omdat we wetendat er een B moet zijn

Om dit te voorkomen, wordt instanceOfB gedeclareerd als een impliciete onverpakte optie (instanceOfB!), en we hebben er toegang toe door alleen instanceOfB te gebruiken. (Bovendien vermoed ikdat de compiler de toegang ook op een andere manier kan optimaliseren).

Een voorbeeld hiervan wordt gegeven op pagina’s 464 tot 466 van het boek.

Samenvatting:

  • Gebruik ? als de waarde in de toekomst nihil kan worden, zodat je hierop gaat testen.
  • Gebruik ! als het in de toekomst echt niet nul mag worden, maar in eerste instantie wel nul moet zijn.

Antwoord 2, autoriteit 11%

Je moet verder gaan dan de syntactische suiker.

Er zijn twee totaal verschillende polymorfe typen. De syntactische suiker gebruikt gewoon een van deze soorten.

Als je Foo?als een type schrijft, heb je echt Optional<Foo>, terwijl je bij Foo!echt ImplicitlyUnwrappedOptional<Foo>.

Dit zijn twee verschillende typen, en ze verschillen ook van Foo.


Antwoord 3, autoriteit 3%

De waarden die u maakt met ?zijn gewone optionele waarden, zoals u al zei, u moet deze openen via optionele binding (if let unwrappedValue = myOptionalValue) of door de uitroep te gebruiken puntsyntaxis myOptionalValue!.doSomething().

De waarden die u maakt met !worden impliciet uitgepakte optionele opties genoemd. Met hen hoeft u ze niet handmatig uit te pakken voordat u ze gebruikt. Wanneer u val myOptionalValue!.doSomething()doet.

De waarde wordt automatisch voor u uitgepakt wanneer u myOptionalValuerechtstreeks gebruikt, wees hier echter voorzichtig mee, want toegang krijgen tot een impliciet uitgepakte waarde terwijl er geen waarde in staat (wanneer het nil) resulteert in een runtime-fout.


Antwoord 4, autoriteit 3%

? (Optioneel)geeft aan dat uw variabele een waarde nul kan bevatten terwijl
! (unwrapper)geeft aan dat uw variabele een geheugen (of waarde) moet hebben wanneer deze tijdens runtime wordt gebruikt (geprobeerd om er een waarde uit te halen).

Het belangrijkste verschil is dat optionele chaining gracieus mislukt wanneer de optionele nul is, terwijl geforceerd uitpakken een runtime-fout veroorzaakt wanneer de optionele nul is.

Om aan te geven dat optionele chaining kan worden aangeroepen op een nulwaarde, is het resultaat van een optionele chaining-aanroep altijd een optionele waarde, zelfs als de eigenschap, methode of subscript die u opvraagt ​​een niet-optionele waarde retourneert. U kunt deze optionele retourwaarde gebruiken om te controleren of de optionele ketenaanroep succesvol was (de geretourneerde optionele waarde bevat een waarde), of niet is geslaagd vanwege een nulwaarde in de keten (de geretourneerde optionele waarde is nul).

In het bijzonder is het resultaat van een optionele ketenaanroep van hetzelfde type als de verwachte retourwaarde, maar verpakt in een optionele. Een eigenschap die normaal een Int retourneert, retourneert een Int?wanneer deze wordt geopend via optionele chaining.

var defaultNil : String?  // declared variable with default nil value
println(defaultNil) >> nil  
var canBeNil : String? = "test"
println(canBeNil) >> optional(test)
canBeNil = nil
println(canBeNil) >> nil
println(canBeNil!) >> // Here nil optional variable is being unwrapped using ! mark (symbol), that will show runtime error. Because a nil optional is being tried to get value using unwrapper
var canNotBeNil : String! = "test"
print(canNotBeNil) >> "test"
var cantBeNil : String = "test"
cantBeNil = nil // can't do this as it's not optional and show a compile time error

Raadpleeg voor meer informatie een document van Apple Ontwikkelaarscommissie, in detail


Antwoord 5, autoriteit 3%

De soort String!wordt een impliciet uitgepakt optioneel:

Soms is het duidelijk uit de structuur van een programma dat een optional altijd een waarde heeft, nadat die waarde voor het eerst is ingesteld. In deze gevallen is het handig om de noodzaak om de waarde van de optie te controleren en uit te pakken elke keer dat deze wordt geopend, weg te nemen, omdat veilig kan worden aangenomen dat deze altijd een waarde heeft.

Dit soort optionele opties wordt gedefinieerd als impliciet onverpakte opties. U schrijft een impliciet onverpakte optional door een uitroepteken (String!) in plaats van een vraagteken (String?) te plaatsen na het type dat u optioneel wilt maken.


Antwoord 6, autoriteit 2%

in de optionele koppelingssectie vindt u het antwoord:

voorbeeld les:

class Person {
    var residence: Residence?
}
class Residence {
    var numberOfRooms = 1
}

Als u probeert toegang te krijgen tot de eigenschap numberOfRooms van de woning van deze persoon, door een uitroepteken achter de woning te plaatsen om het uitpakken van de waarde te forceren, activeert u een runtime-fout, omdat er geen woningwaarde is om uit te pakken:

let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error

De bovenstaande code werkt wanneer john.residence een waarde heeft die niet nul is en roomCount instelt op een Int-waarde die het juiste aantal kamers bevat. Deze code activeert echter altijd een runtime-fout wanneer de woonplaats nul is, zoals hierboven geïllustreerd.

Optioneel koppelen biedt een alternatieve manier om toegang te krijgen tot de waarde van numberOfRooms. Gebruik een vraagteken in plaats van het uitroepteken om optionele kettingen te gebruiken:

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

Antwoord 7

Goed vermeld door @tarmes hierboven. Merkte een ander gebruik van impliciete optioneel op:

Laten we zeggen dat ik een optionele Intheb:

let firstInt: Int? = 9

En ik probeer optionele patroonovereenkomst te gebruiken en deze optionele Intals volgt te gebruiken:

if case let myFirstInt? = firstInt where myFirstInt > 1 {
    print("Valid")
} else {
    print("Invalid")
}

Merk op dat ik impliciete optioneel gebruik met lokale parameter myFirstIntwaardoor het veilig is voor nilvoorwaarde gekoppeld aan optionele firstInt. Als ik nu firstIntmaak als nil, zal het else-voorwaarde uitvoeren. Als ik in plaats daarvan force-unwrap gebruik met firstIntdat zou leiden tot een crash, ongeveer als volgt:

voer hier de afbeeldingsbeschrijving in

Other episodes