问题
The Swift Programming Language guide has the following example:
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)
Then when assigning the apartment to the person, they use an exclamation point to \"unwrap the instance\":
john!.apartment = number73
What does it mean to \"unwrap the instance\"? Why is it necessary? How is it different from just doing the following:
john.apartment = number73
I\'m very new to the Swift language. Just trying to get the basics down.
UPDATE:
The big piece of the puzzle that I was missing (not directly stated in the answers - at least not at the time of writing this) is that when you do the following:
var john: Person?
that does NOT mean that \"john
is of type Person
and it might be nil\", as I originally thought. I was simply misunderstanding that Person
and Person?
are completely separate types. Once I grasped that, all of the other ?
, !
madness, and the great answers below, made a lot more sense.
回答1:
What does it mean to "unwrap the instance"? Why is it necessary?
As far as I can work out (this is very new to me, too)...
The term "wrapped" implies we should think of an Optional variable as a present, wrapped in shiny paper, which might (sadly!) be empty.
When "wrapped", the value of an Optional variable is an enum with two possible values (a little like a Boolean). This enum describes whether the variable holds a value (Some(T)
), or not (None
).
If there is a value, this can be obtained by "unwrapping" the variable (obtaining the T
from Some(T)
).
How is
john!.apartment = number73
different fromjohn.apartment = number73
? (Paraphrased)
If you write the name of an Optional variable (eg text john
, without the !
), this refers to the "wrapped" enum (Some/None), not the value itself (T). So john
isn't an instance of Person
, and it doesn't have an apartment
member:
john.apartment
// 'Person?' does not have a member named 'apartment'
The actual Person
value can be unwrapped in various ways:
- "forced unwrapping":
john!
(gives thePerson
value if it exists, runtime error if it is nil) - "optional binding":
if let p = john { println(p) }
(executes theprintln
if the value exists) - "optional chaining":
john?.learnAboutSwift()
(executes this made-up method if the value exists)
I guess you choose one of these ways to unwrap, depending upon what should happen in the nil case, and how likely that is. This language design forces the nil case to be handled explicitly, which I suppose improves safety over Obj-C (where it is easy to forget to handle the nil case).
Update:
The exclamation mark is also used in the syntax for declaring "Implicitly Unwrapped Optionals".
In the examples so far, the john
variable has been declared as var john:Person?
, and it is an Optional. If you want the actual value of that variable, you must unwrap it, using one of the three methods above.
If it were declared as var john:Person!
instead, the variable would be an Implicitly Unwrapped Optional (see the section with this heading in Apple's book). There is no need to unwrap this kind of variable when accessing the value, and john
can be used without additional syntax. But Apple's book says:
Implicitly unwrapped optionals should not be used when there is a possibility of a variable becoming nil at a later point. Always use a normal optional type if you need to check for a nil value during the lifetime of a variable.
Update 2:
The article "Interesting Swift Features" by Mike Ash gives some motivation for optional types. I think it is great, clear writing.
Update 3:
Another useful article about the implicitly unwrapped optional use for the exclamation mark: "Swift and the Last Mile" by Chris Adamson. The article explains that this is a pragmatic measure by Apple used to declare the types used by their Objective-C frameworks which might contain nil. Declaring a type as optional (using ?
) or implicitly unwrapped (using !
) is "a tradeoff between safety and convenience". In the examples given in the article, Apple have chosen to declare the types as implicitly unwrapped, making the calling code more convenient, but less safe.
Perhaps Apple might comb through their frameworks in the future, removing the uncertainty of implicitly unwrapped ("probably never nil") parameters and replacing them with optional ("certainly could be nil in particular [hopefully, documented!] circumstances") or standard non-optional ("is never nil") declarations, based on the exact behaviour of their Objective-C code.
回答2:
Here is what I think is the difference:
var john: Person?
Means john can be nil
john?.apartment = number73
The compiler will interpret this line as:
if john != nil {
john.apartment = number73
}
While
john!.apartment = number73
The compiler will interpret this line as simply:
john.apartment = number73
Hence, using ! will unwrap the if statement, and make it run faster, but if john is nil, then a runtime error will happen.
So wrap here doesn't mean it is memory wrapped, but it means it is code wrapped, in this case it is wrapped with an if statement, and because Apple pay close attention to performance in runtime, they want to give you a way to make your app run with the best possible performance.
Update:
Getting back to this answer after 4 years, as I got the highest reputations from it in Stackoverflow :) I misunderstood a little the meaning of unwrapping at that time. Now after 4 years I believe the meaning of unwrapping here is to expand the code from its original compact form. Also it means removing the vagueness around that object, as we are not sure by definition of it is nil or not. Just like the answer of Ashley above, think about it as a present which could contain nothing in it. But I still think that the unwrapping is code unwrapping and not memory based unwrapping as using enum.
回答3:
TL;DR
What does an exclamation mark mean in the Swift language?
The exclamation mark effectively says, “I know that this optional definitely has a value; please use it.” This is known as forced unwrapping of the optional’s value:
Example
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."
Source: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399
回答4:
If john were an optional var (declared thusly)
var john: Person?
then it would be possible for john to have no value (in ObjC parlance, nil value)
The exclamation point basically tells the compiler "I know this has a value, you don't need to test for it". If you didn't want to use it, you could conditionally test for it:
if let otherPerson = john {
otherPerson.apartment = number73
}
The interior of this will only evaluate if john has a value.
回答5:
Some big picture perspective to add to the other useful but more detail-centric answers:
In Swift, the exclamation point appears in several contexts:
- Forced unwrapping:
let name = nameLabel!.text
- Implicitly unwrapped optionals:
var logo: UIImageView!
- Forced casting:
logo.image = thing as! UIImage
- Unhandled exceptions:
try! NSJSONSerialization.JSONObjectWithData(data, [])
Every one of these is a different language construct with a different meaning, but they all have three important things in common:
1. Exclamation points circumvent Swift’s compile-time safety checks.
When you use !
in Swift, you are essentially saying, “Hey, compiler, I know you think an error could happen here, but I know with total certainty that it never will.”
Not all valid code fits into the box of Swift’s compile-time type system — or any language’s static type checking, for that matter. There are situations where you can logically prove that an error will never happen, but you can’t prove it to the compiler. That’s why Swift’s designers added these features in the first place.
However, whenever you use !
, you’re ruling out having a recovery path for an error, which means that…
2. Exclamation points are potential crashes.
An exclamation point also says, “Hey Swift, I am so certain that this error can never happen that it’s better for you to crash my whole app than it is for me to code a recovery path for it.”
That’s a dangerous assertion. It can be the correct one: in mission-critical code where you have thought hard about your code’s invariants, it may be that bogus output is worse than a crash.
However, when I see !
in the wild, it's rarely used so mindfully. Instead, it too often means, “this value was optional and I didn’t really think too hard about why it could be nil or how to properly handle that situation, but adding !
made it compile … so my code is correct, right?”
Beware the arrogance of the exclamation point. Instead…
3. Exclamation points are best used sparingly.
Every one of these !
constructs has a ?
counterpart that forces you to deal with the error/nil case:
- Conditional unwrapping:
if let name = nameLabel?.text { ... }
- Optionals:
var logo: UIImageView?
- Conditional casts:
logo.image = thing as? UIImage
- Nil-on-failure exceptions:
try? NSJSONSerialization.JSONObjectWithData(data, [])
If you are tempted to use !
, it is always good to consider carefully why you are not using ?
instead. Is crashing your program really the best option if the !
operation fails? Why is that value optional/failable?
Is there a reasonable recovery path your code could take in the nil/error case? If so, code it.
If it can’t possibly be nil, if the error can never happen, then is there a reasonable way to rework your logic so that the compiler knows that? If so, do it; your code will be less error-prone.
There are times when there is no reasonable way to handle an error, and simply ignoring the error — and thus proceeding with wrong data — would be worse than crashing. Those are the times to use force unwrapping.
I periodically search my entire codebase for !
and audit every use of it. Very few usages stand up to scrutiny. (As of this writing, the entire Siesta framework has exactly two instances of it.)
That’s not to say you should never use !
in your code — just that you should use it mindfully, and never make it the default option.
回答6:
john
is an optional var
. So can be contains a nil
value. To ensure that the value isn't nil use a !
at the end of the var
name.
From documentation
“Once you’re sure that the optional does contain a value, you can access its underlying value by adding an exclamation mark (!) to the end of the optional’s name. The exclamation mark effectively says, “I know that this optional definitely has a value; please use it.”
Another way to check non nil value is
if let j = json {
// do something with j
}
回答7:
Here are some examples:
var name:String = "Hello World"
var word:String?
Where word
is an optional value. means it may or may not contain a value.
word = name
Here name
has a value so we can assign it
var cow:String = nil
var dog:String!
Where dog
is forcefully unwrapped means it must contain a value
dog = cow
The application will crash because we are assign nil
to unwrapped
回答8:
In this case...
var John: Person!
it means, that initially John will have nil value, it will be set and once set will never be nil-led again. Therefore for convenience I can use the easier syntax for accessing an optional var because this is an "Implicitly unwrapped optional"
回答9:
If you've come from a C-family language, you will be thinking "pointer to object of type X which might be the memory address 0 (NULL)", and if you're coming from a dynamically typed language you'll be thinking "Object which is probably of type X but might be of type undefined". Neither of these is actually correct, although in a roundabout way the first one is close.
The way you should be thinking of it is as if it's an object like:
struct Optional<T> {
var isNil:Boolean
var realObject:T
}
When you're testing your optional value with foo == nil
it's really returning foo.isNil
, and when you say foo!
it's returning foo.realObject
with an assertion that foo.isNil == false
. It's important to note this because if foo
actually is nil when you do foo!
, that's a runtime error, so typically you'd want to use a conditional let instead unless you are very sure that the value will not be nil. This kind of trickery means that the language can be strongly typed without forcing you to test if values are nil everywhere.
In practice, it doesn't truly behave like that because the work is done by the compiler. At a high level there is a type Foo?
which is separate to Foo
, and that prevents funcs which accept type Foo
from receiving a nil value, but at a low level an optional value isn't a true object because it has no properties or methods; it's likely that in fact it is a pointer which may by NULL(0) with the appropriate test when force-unwrapping.
There other situation in which you'd see an exclamation mark is on a type, as in:
func foo(bar: String!) {
print(bar)
}
This is roughly equivalent to accepting an optional with a forced unwrap, i.e.:
func foo(bar: String?) {
print(bar!)
}
You can use this to have a method which technically accepts an optional value but will have a runtime error if it is nil. In the current version of Swift this apparently bypasses the is-not-nil assertion so you'll have a low-level error instead. Generally not a good idea, but it can be useful when converting code from another language.
回答10:
The ! means that you are force unwrapping the object the ! follows. More info can be found in Apples documentation, which can be found here: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html
回答11:
If you're familiar with C#, this is like Nullable types which are also declared using a question mark:
Person? thisPerson;
And the exclamation mark in this case is equivalent to accessing the .Value property of the nullable type like this:
thisPerson.Value
回答12:
In objective C variables with no value were equal to 'nil'(it was also possible to use 'nil' values same as 0 and false), hence it was possible to use variables in conditional statements (Variables having values are same as 'TRUE' and those with no values were equal to 'FALSE').
Swift provides type safety by providing 'optional value'. i.e. It prevents errors formed from assigning variables of different types.
So in Swift, only booleans can be provided on conditional statements.
var hw = "Hello World"
Here, even-though 'hw' is a string, it can't be used in an if statement like in objective C.
//This is an error
if hw
{..}
For that it needs to be created as,
var nhw : String? = "Hello World"
//This is correct
if nhw
{..}
回答13:
The ! at the end of an object says the object is an optional and to unwrap if it can otherwise returns a nil. This is often used to trap errors that would otherwise crash the program.
回答14:
In Short (!): After you have declare a variable and that you are certain the variable is holding a value.
let assumedString: String! = "Some message..."
let implicitString: String = assumedString
else you would have to do this on every after passing value...
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
回答15:
John is an optional Person, meaning it can hold a value or be nil.
john.apartment = number73
is used if john is not an optional. Since john is never nil we can be sure it won't call apartment on a nil value. While
john!.apartment = number73
promises the compiler that john is not nil then unwraps the optional to get john's value and accesses john's apartment property. Use this if you know that john is not nil. If you call this on a nil optional, you'll get a runtime error.
The documentation includes a nice example for using this where convertedNumber is an optional.
if convertedNumber {
println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
println("\(possibleNumber) could not be converted to an integer")
}
回答16:
To put it simply, exclamation marks mean an optional is being unwrapped. An optional is a variable that can have a value or not -- so you can check if the variable is empty, using an if let statement as shown here, and then force unwrap it. If you force unwrap an optional that is empty though, your program will crash, so be careful! Optionals are declared by putting a question mark at the end of an explicit assignment to a variable, for example I could write:
var optionalExample: String?
This variable has no value. If I were to unwrap it, the program would crash and Xcode would tell you you tried to unwrap an optional with a value of nil.
Hope that helped.
回答17:
IN SIMPLE WORDS
USING Exclamation mark indicates that variable must consists non nil value (it never be nil)
回答18:
The entire story begins with a feature of swift called optional vars. These are the vars which may have a value or may not have a value. In general swift doesn't allow us to use a variable which isn't initialised, as this may lead to crashes or unexpected reasons and also server a placeholder for backdoors. Thus in order to declare a variable whose value isn't initially determined we use a '?'. When such a variable is declared, to use it as a part of some expression one has to unwrap them before use, unwrapping is an operation through which value of a variable is discovered this applies to objects. Without unwrapping if you try to use them you will have compile time error. To unwrap a variable which is an optional var, exclamation mark "!" is used.
Now there are times when you know that such optional variables will be assigned values by system for example or your own program but sometime later , for example UI outlets, in such situation instead of declaring an optional variable using a question mark "?" we use "!".
Thus system knows that this variable which is declared with "!" is optional right now and has no value but will receive a value in later in its lifetime.
Thus exclamation mark holds two different usages, 1. To declare a variable which will be optional and will receive value definitely later 2. To unwrap an optional variable before using it in an expression.
Above descriptions avoids too much of technical stuff, i hope.
回答19:
If you use it as an optional, it unwraps the optional and sees if something is there. If you use it in an if-else statement is is code for NOT. For example,
if (myNumber != 3){
// if myNumber is NOT 3 do whatever is inside these brackets.
)
回答20:
An Optional variable may contain a value or may be not
case 1: var myVar:String? = "Something"
case 2: var myVar:String? = nil
now if you ask myVar!, you are telling compiler to return a value in case 1 it will return "Something"
in case 2 it will crash.
Meaning ! mark will force compiler to return a value, even if its not there. thats why the name Force Unwrapping.
回答21:
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
回答22:
ASK YOURSELF
- Does the type
person?
have anapartment
member/property? OR - Does the type
person
have anapartment
member/property?
If you can't answer this question, then continue reading:
To understand you may need super-basic level of understanding of Generics. See here. A lot of things in Swift are written using Generics. Optionals included
The code below has been made available from this Stanford video. Highly recommend you to watch the first 5 minutes
An Optional is an enum with only 2 cases
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
}
Optional binding:
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
}
when you say var john: Person?
You actually mean such:
enum Optional<Person>{
case .None
case .Some(Person)
}
Does the above enum have any property named apartment
? Do you see it anywhere? It's not there at all! However if you unwrap it ie do person!
then you can ... what it does under the hood is : Optional<Person>.Some(Person(name: "John Appleseed"))
Had you defined var john: Person
instead of: var john: Person?
then you would have no longer needed to have the !
used, because Person
itself does have a member of apartment
As a future discussion on why using !
to unwrap is sometimes not recommended see this Q&A
来源:https://stackoverflow.com/questions/24018327/what-does-an-exclamation-mark-mean-in-the-swift-language