What does an exclamation mark mean in the Swift language?

后端 未结 22 2164
南方客
南方客 2020-11-22 03:47

The Swift Programming Language guide has the following example:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apar         


        
相关标签:
22条回答
  • 2020-11-22 04:31

    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 from john.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 the Person value if it exists, runtime error if it is nil)
    • "optional binding": if let p = john { println(p) } (executes the println 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.

    0 讨论(0)
  • 2020-11-22 04:34

    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.

    0 讨论(0)
  • 2020-11-22 04:36

    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.
    )
    
    0 讨论(0)
  • 2020-11-22 04:36

    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

    0 讨论(0)
提交回复
热议问题