What does an exclamation mark mean in the Swift la

2018-12-31 06:29发布

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.

22条回答
情到深处是孤独
2楼-- · 2018-12-31 06:46

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.

查看更多
牵手、夕阳
3楼-- · 2018-12-31 06:47

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

 {..}
查看更多
梦寄多情
4楼-- · 2018-12-31 06:48

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楼-- · 2018-12-31 06:48

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.

查看更多
呛了眼睛熬了心
6楼-- · 2018-12-31 06:49

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

查看更多
不流泪的眼
7楼-- · 2018-12-31 06:50

ASK YOURSELF

  • Does the type person? have an apartment member/property? OR
  • Does the type person have an apartment 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

查看更多
登录 后发表回答