首页 文章

Swift语言中的感叹号是什么意思?

提问于
浏览
490

The Swift Programming Language guide有以下示例:

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)

然后,当将公寓分配给该人时,他们使用感叹号来“解开实例”:

john!.apartment = number73

“打开实例”是什么意思?为什么有必要?它与仅执行以下操作有何不同:

john.apartment = number73

我对Swift语言很新 . 只是试图让基础知识失败 .

UPDATE:
我缺少的这个难题的大部分(在答案中没有直接说明 - 至少在撰写本文时没有说明)是当你做以下事情时:

var john: Person?

这并不意味着“ john 的类型为 Person ,它可能是零”,正如我原先想的那样 . 我只是误解了 PersonPerson? 是完全不同的类型 . 一旦我掌握了这一点,所有其他的疯狂,以及下面的重大答案,都更有意义 .

22 回答

  • 2

    在目标C中,没有值的变量等于'nil'(也可以使用与0和false相同的'nil'值),因此可以在条件语句中使用变量(具有值的变量与'TRUE相同'和那些没有 Value 观的人等于'假') .

    Swift通过提供“可选值”来提供类型安全性 . 即它可以防止因分配不同类型的变量而形成的错误 .

    所以在Swift中,只能在条件语句中提供布尔值 .

    var hw = "Hello World"
    

    在这里,偶数'虽然'hw'是一个字符串,但它不能在if语句中使用,就像在Objective C中一样 .

    //This is an error
    
    if hw
    
     {..}
    

    为此,它需要创建为,

    var nhw : String? = "Hello World"
    
    //This is correct
    
    if nhw
    
     {..}
    
  • 1

    的!在对象的末尾表示该对象是可选的,并且如果否则返回nil则展开 . 这通常用于捕获可能导致程序崩溃的错误 .

  • 24

    “打开实例”是什么意思?为什么有必要?

    据我所知(这对我来说也很新)......

    "wrapped"这个词意味着我们应该 think of an Optional variable as a present, wrapped in shiny paper, which might (sadly!) be empty .

    当"wrapped"时,Optional变量的值是一个带有两个可能值的枚举(有点像布尔值) . 此枚举描述变量是否包含值( Some(T) )或不包含( None ) .

    如果有值,则可以通过"unwrapping"变量获取(从 Some(T) 获取 T ) .

    约翰!.apartment = number73与john.apartment = number73有什么不同? (意译)

    如果你写了一个Optional变量的名字(例如文本 john ,没有 ! ),这就是"wrapped"枚举(Some / None),而不是值本身(T) . 所以 john 不是 Person 的实例,它没有 apartment 成员:

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

    实际 Person 值可以通过各种方式解包:

    • "forced unwrapping": john! (如果存在,则给出 Person 值,如果为零则给出运行时错误)

    • "optional binding": if let p = john { println(p) } (如果值存在,则执行 println

    • "optional chaining": john?.learnAboutSwift() (如果值存在,则执行此制作方法)

    我想你选择其中一种方法来打开,取决于在nil情况下会发生什么,以及它有多大可能 . 这种语言设计强制显式处理nil case,我想这可以提高Obj-C的安全性(很容易忘记处理nil的情况) .

    Update

    感叹号也在语法中用于声明“隐式解包的选项” .

    在到目前为止的示例中, john 变量已声明为 var john:Person? ,并且它是可选的 . 如果您想要该变量的实际值,则必须使用上述三种方法之一解包它 .

    如果它被声明为 var john:Person! ,则该变量将是一个隐式解包可选(请参阅Apple书中带有此 Headers 的部分) . 访问该值时无需解包此类变量,并且无需其他语法即可使用 john . 但苹果公司的书说:

    如果变量有可能在以后变为零,则不应使用隐式展开的选项 . 如果需要在变量的生命周期内检查nil值,请始终使用普通的可选类型 .

    Update 2

    Mike Ash撰写的文章“Interesting Swift Features”为可选类型提供了一些动力 . 我认为这很好,写得很清楚 .

    Update 3

    另一个有用的文章关于感叹号的隐式解包可选用法:Chris Adamson撰写的“Swift and the Last Mile” . 文章解释说,这是Apple用来声明其Objective-C框架使用的类型的实用指标,这些框架可能包含nil . 将类型声明为可选(使用 ? )或隐式解包(使用 ! )是"a tradeoff between safety and convenience" . 在本文给出的示例中,Apple选择将类型声明为隐式解包,使调用代码更方便,但安全性更低 .

    也许苹果可能会在未来梳理他们的框架,消除隐含地展开(“可能永远不会”)参数的不确定性,并用可选的替换它们(“肯定可能是零[希望,有记录!]情况”)或标准非-optional(“永远不会是”)声明,基于其Objective-C代码的确切行为 .

  • -1

    简而言之(!):声明变量并确定变量保持值后 .

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

    否则你必须在每次传递 Value 后做到这一点......

    let possibleString: String? = "An optional string."
    let forcedString: String = possibleString! // requires an exclamation mark
    
  • 62

    以下是我认为的不同之处:

    var john: Person?
    

    意味着约翰可以是零

    john?.apartment = number73
    

    编译器会将此行解释为:

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

    john!.apartment = number73
    

    编译器将简单地解释此行:

    john.apartment = number73
    

    因此,使用!将解包if语句,并使其运行得更快,但如果john为nil,则会发生运行时错误 .

    所以这里的包装并不意味着它是内存包装的,但它意味着它是代码包装的,在这种情况下它用if语句包装,并且因为Apple在运行时密切关注性能,他们想给你一个方法让您的应用以最佳性能运行 .

    Update:

    4之后回到这个答案多年来,我在Stackoverflow中获得了最高的声誉:)我误解了当时展开的意义 . 现在4年后,我相信在这里展开的意思是将代码从原始的紧凑形式扩展 . 此外,它意味着消除该对象周围的模糊性,因为我们不确定它的定义是否为零 . 就像上面Ashley的回答一样,把它想象成一个可以包含任何内容的礼物 . 但我仍然认为解包是代码解包而不是基于内存的解包使用枚举 .

  • 2

    TL; DR

    Swift语言中的感叹号是什么意思?

    感叹号有效地说:“我知道这个选项肯定有 Value ;请使用它 . “这被称为强制解包可选的值:

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

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

  • 500

    如果john是一个可选的var(由此声明)

    var john: Person?
    

    那么约翰就有可能没有 Value (用ObjC说法,零值)

    感叹号基本上告诉编译器“我知道这有一个值,你不需要测试它” . 如果您不想使用它,您可以有条件地测试它:

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

    这个内部只会评估john是否有值 .

  • 1

    一些大图片视角可以添加到其他有用但更详细的中心答案:

    在Swift中,感叹号出现在几个上下文中:

    • 强制展开: let name = nameLabel!.text

    • 隐式解包期权: var logo: UIImageView!

    • 强迫铸造: logo.image = thing as! UIImage

    • 未处理的例外情况: try! NSJSONSerialization.JSONObjectWithData(data, [])

    这些中的每一个都是不同的语言结构,具有不同的含义,但它们都有三个重要的共同点:

    1.感叹号绕过Swift的编译时安全检查 .

    当你在Swift中使用 ! 时,你基本上是在说,“嘿,编译器,我知道你认为这里可能会发生错误,但我完全确定它永远都不会 . ”

    并非所有有效代码都适合Swift的编译时类型系统 - 或者任何语言的静态类型检查 . 在某些情况下,您可以逻辑地证明错误永远不会发生,但您无法向编译器证明 . 这就是为什么Swift的设计师首先添加了这些功能 .

    但是,无论何时使用 ! ,您都要排除错误的恢复路径,这意味着......

    2.感叹号是潜在的崩溃 .

    一个感叹号还说,“嘿,斯威夫特,我非常肯定这个错误永远不会发生,因为你让我的整个应用程序崩溃比为我编写恢复路径更好 . ”

    这是一个危险的断言 . 它可能是正确的:在任务关键代码中,您已经仔细考虑过代码的不变量,可能是虚假输出比崩溃更糟糕 .

    然而,当我在野外看到 ! 时,它很少被如此谨慎地使用 . 相反,它经常意味着,“这个值是可选的,我并不是真的想太多为什么它可能是零或如何正确处理这种情况,但添加 ! 使它编译...所以我的代码是正确的,对吧? ”

    要小心这个感叹号的傲慢 . 代替…

    3.最好谨慎使用感叹号 .

    这些 ! 构造中的每一个都有一个 ? 对应物,迫使你处理error / nil情况:

    • 有条件的展开: if let name = nameLabel?.text { ... }

    • 可选项: var logo: UIImageView?

    • 有条件的演员表: logo.image = thing as? UIImage

    • 无失败例外: try? NSJSONSerialization.JSONObjectWithData(data, [])

    如果您想使用 ! ,那么请务必仔细考虑为什么不使用 ? . 如果 ! 操作失败,崩溃你的程序真的是最好的选择吗? Why is that value optional/failable?

    在nil / error情况下,代码可以采用合理的恢复路径吗?如果是这样,请编码 .

    如果它不可能是nil,如果错误永远不会发生,那么有没有合理的方法来重写你的逻辑,以便编译器知道?如果是这样,那就去做;您的代码不会出错 .

    有时候没有合理的方法来处理错误,而只是忽略错误 - 从而继续处理错误的数据 - 会比崩溃更糟糕 . 那些是使用力展开的时代 .

    我会定期在整个代码库中搜索 ! 并审核它的每次使用情况 . 很少有用法经得起审查 . (截至撰写本文时,整个Siesta框架完全具有two instances . )

    这并不是说你不应该在你的代码中使用 ! - 只是你应该谨慎使用它,而不要把它作为默认选项 .

  • 26

    john 是可选的 var . 所以可以包含一个 nil 值 . 要确保该值不是nil,请在 var 名称的末尾使用 ! .

    从文档

    “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.”

    另一种检查非零值的方法是

    if let j = json {
            // do something with j
        }
    
  • 122

    这里有些例子:

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

    其中 word 是可选值 . 意味着它可能包含也可能不包含值 .

    word = name
    

    这里 name 有一个值,所以我们可以分配它

    var cow:String = nil
    var dog:String!
    

    强行打开 dog 意味着它必须包含一个值

    dog = cow
    

    应用程序将崩溃,因为我们将 nil 指定为unwrapped

  • 13

    在这种情况下...

    var John:人!

    这意味着,最初John将拥有零值,它将被设置并且一旦设置将永远不会再次为零 . 因此,为了方便起见,我可以使用更简单的语法来访问可选的var,因为这是一个“隐式解包的可选”

  • 3

    如果您来自C系列语言,您将会想到“指向X类型的对象的指针,它可能是内存地址0(NULL)”,如果您来自动态类型语言,那么您将会思考“可能是X型但可能是未定义类型的对象” . 这些都不是正确的,尽管以迂回的方式第一个接近 .

    你应该考虑它的方式就好像它是一个像这样的对象:

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

    当您使用 foo == nil 测试可选值时,它确实返回 foo.isNil ,当您说 foo! 时,它返回 foo.realObject ,其断言为 foo.isNil == false . 重要的是要注意这一点,因为如果你做 foo!foo 实际上是nil,那么's a runtime error, so typically you' d想要使用条件let,除非你非常确定该值不是nil . 这种欺骗意味着语言可以强类型,而不必强迫您测试值是否为零 .

    在实践中,它并不真正表现得那样,因为工作是由编译器完成的 . 在高级别有一个类型 Foo? ,它与 Foo 分开,并且阻止接受类型 Foo 的func接收nil值,但是在低级别,可选值isn 't a true object because it has no properties or methods; it' s可能实际上它是一个指针,可能当强制展开时,通过NULL(0)进行适当的测试 .

    在其他情况下,您会在类型上看到感叹号,如:

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

    这大致相当于接受强制展开的可选项,即:

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

    您可以使用此方法来获得一个技术上接受可选值的方法,但如果它是nil则会有运行时错误 . 在当前版本的Swift中,这显然绕过了is-not-nil断言,因此你会遇到一个低级错误 . 通常不是一个好主意,但在从另一种语言转换代码时它可能很有用 .

  • 16

    的!意味着你强行打开物体!如下 . 更多信息可以在苹果文档中找到,可以在这里找到:https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html

  • 1

    如果您熟悉C#,这就像Nullable类型,它们也使用问号声明:

    Person? thisPerson;
    

    在这种情况下,感叹号相当于访问可空类型的.Value属性,如下所示:

    thisPerson.Value
    
  • 0

    John是一个可选的Person,意思是它可以保持一个值或为零 .

    john.apartment = number73
    

    如果john不是可选的,则使用 . 由于约翰永远不会是零,我们可以肯定它不会以零值称公寓 . 而

    john!.apartment = number73
    

    承诺编译器john不是nil然后解包可选的获取john的值并访问john的公寓属性 . 如果你知道约翰不是零,请使用此选项 . 如果你在一个nil可选项上调用它,你将收到运行时错误 .

    该文档包含一个很好的例子,用于使用convertNumber是可选的 .

    if convertedNumber {
        println("\(possibleNumber) has an integer value of \(convertedNumber!)")
    } else {
        println("\(possibleNumber) could not be converted to an integer")
    }
    
  • 3

    简单地说,感叹号意味着可选的包装被打开 . 可选是一个可以有值的变量 - 所以你可以使用if let语句as shown here来检查变量是否为空,然后强制解包它 . 如果您强制打开一个空的可选项,程序将崩溃,所以要小心!通过在变量的显式赋值的末尾添加一个问号来声明Optionals,例如我可以写:

    var optionalExample: String?
    

    这个变量没有 Value . 如果我打开它,那么程序会崩溃,Xcode会告诉你你试图打开一个值为nil的可选项 .

    希望有所帮助 .

  • 1

    IN SIMPLE WORDS

    USING Exclamation mark indicates that variable must consists non nil value (it never be nil)

  • 2

    整个故事的开头是一个叫做可选变量的快速特征 . 这些是可能具有值或可能没有值的变量 . 通常,swift不允许我们使用未初始化的变量,因为这可能导致崩溃或意外原因,并且还为后门服务器占位符 . 因此,为了声明一个最初未确定其值的变量,我们使用'?' . 当声明这样的变量时,要将其用作某个表达式的一部分,必须在使用前解包它们,解包是一个操作,通过该操作可以发现变量的值,这适用于对象 . 如果您尝试使用它们而不解包,则会出现编译时错误 . 要打开一个可选var变量,感叹号“!”用来 .

    现在有些时候你知道这些可选变量将被系统分配值,例如你自己的程序,但稍后某些时候,例如UI出口,在这种情况下,而不是使用问号“?”声明可选变量 . 我们用 ”!” .

    因此系统知道这个变量用“!”声明现在是可选的,没有任何 Value ,但会在其生命周期的后期收到一个值 .

    因此,感叹号有两种不同的用法,1 . 声明一个可选的变量,并且稍后会明确地接收值2.在表达式中使用它之前解开一个可选变量 .

    我希望上面的描述避免了太多技术性的东西 .

  • 4

    如果您将其用作可选项,它会打开可选项并查看是否存在某些内容 . 如果你在if-else语句中使用它是NOT的代码 . 例如,

    if (myNumber != 3){
     // if myNumber is NOT 3 do whatever is inside these brackets.
    )
    
  • 37

    Optional变量可能包含值,也可能不包含值

    案例1: var myVar:String? = "Something"

    案例2: var myVar:String? = nil

    现在,如果你问myVar !,你告诉编译器返回一个值,如果它将返回 "Something"

    在案例2中它会崩溃 .

    意思! mark会强制编译器返回一个值,即使它不存在 . 这就是为什么名字 Force Unwrapping .

  • 1
    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
    
  • 1

    ASK YOURSELF

    • 类型 person? 是否具有 apartment 成员/属性?要么

    • 类型 person 是否具有 apartment 成员/属性?

    如果您无法回答此问题,请继续阅读:

    要了解您可能需要对泛型的超基本理解 . 看这里 . Swift中的很多东西都是用泛型编写的 . 包括选项

    以下代码可从this Stanford video获得 . 强烈建议您观看前5分钟

    一个可选是仅包含2个案例的枚举

    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
    }
    

    可选绑定:

    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
    }
    

    当你说 var john: Person? 你的意思是这样的:

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

    上面的枚举是否有任何名为 apartment 的属性?你在任何地方看到它吗?那就是 not !然而,如果你打开它,即做 person! 然后你可以...它在引擎盖下做的是: Optional<Person>.Some(Person(name: "John Appleseed"))


    如果您定义了 var john: Person 而不是: var john: Person? 那么您将不再需要使用 ! ,因为 Person 本身确实拥有 apartment 的成员


    作为将来有关使用 ! 解包的原因,有时不建议参见this Q&A

相关问题