Swift编程语言指南中有如下示例:

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语言很不熟悉。我只是想把基本的东西记下来。

更新: 我遗漏了一个很大的谜团(答案中没有直接说明——至少在写这篇文章的时候没有),那就是当你做以下事情的时候:

var john: Person?

这并不意味着“约翰是人的类型,它可能是nil”,因为我最初的想法。我只是误解了《人与人》是完全不同的类型。一旦我明白了,所有其他的?,!疯狂,以及下面这些精彩的回答,让我觉得更有道理。


当前回答

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

据我所知(这对我来说也很陌生)……

术语“包装”意味着我们应该把一个可选变量想象成一个礼物,包裹在闪亮的纸里,可能是空的(很遗憾!)。

当“包装”时,可选变量的值是一个包含两个可能值的enum(有点像布尔值)。这个枚举描述了变量是否包含一个值(Some(T)),或者是否包含一个值(None)。

如果有值,则可以通过“展开”变量来获得(从Some(T)中获取T)。

约翰好吗?73号公寓和约翰不一样。公寓= 73号?(转述)

如果你写一个可选变量的名字(例如文本john,不带!),这指的是“包装”enum (Some/None),而不是值本身(T)。所以john不是Person的实例,它没有公寓成员:

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

实际的Person值可以通过多种方式展开:

“强制拆包”:约翰!(如果Person值存在,则给出Person值,如果为nil则出现运行时错误) "optional binding": if let p = John {println(p)}(如果值存在则执行println) "optional chaining": john?.learnAboutSwift()(如果值存在,则执行这个创建的方法)

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

更新:

感叹号也用于声明“隐式未包装的可选选项”的语法中。

在到目前为止的例子中,john变量被声明为var john:Person?,它是可选的。如果需要该变量的实际值,则必须使用上述三种方法之一展开它。

如果它被声明为var john:Person!相反,该变量将是一个隐式未包装的可选变量(参见Apple book中带有此标题的部分)。在访问值时不需要展开这类变量,并且可以在不使用其他语法的情况下使用john。但苹果的书说:

当变量有可能在稍后变为nil时,不应该使用隐式打开的可选选项。如果您需要在变量的生命周期内检查nil值,请始终使用正常的可选类型。

更新2:

Mike Ash的文章“有趣的Swift特性”为可选类型提供了一些动力。我觉得写得很好,很清楚。

更新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.

也许Apple会在未来梳理他们的框架,删除隐式展开参数(“可能永远不会为nil”)的不确定性,并将它们替换为可选的(“当然可能是nil,特别是[希望有文档!)或标准的非可选(“永不为空”)声明,这取决于它们的Objective-C代码的确切行为。

其他回答

以下是我认为的区别:

var john: Person?

意味着john可以是nil

john?.apartment = number73

编译器将这一行解释为:

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

john!.apartment = number73

编译器将简单地解释这一行:

john.apartment = number73

因此,使用!将打开if语句,并使其运行得更快,但如果John为nil,则会发生运行时错误。

这里的wrap并不是指它是内存包装,而是指它是代码包装,在这种情况下,它是用if语句包装的,因为苹果非常关注运行时的性能,他们想给你一种方法让你的应用以最好的性能运行。

更新:

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

如果你熟悉c#,这就像用问号声明的Nullable类型:

Person? thisPerson;

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

thisPerson.Value

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.

有时候你知道这样的可选变量会被系统或者你自己的程序赋值,但是稍后,比如UI outlet,在这种情况下,我们不会用问号"?"来声明可选变量,而是用"!"

因此,系统知道这个用“!”声明的变量现在是可选的,没有值,但将在其生命周期的后期接收一个值。

因此,感叹号有两种不同的用法, 1. 声明一个变量,该变量将是可选的,并且以后一定会接收值 2. 在表达式中使用可选变量之前,将其展开。

以上描述避免了太多的技术问题,我希望。

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

据我所知(这对我来说也很陌生)……

术语“包装”意味着我们应该把一个可选变量想象成一个礼物,包裹在闪亮的纸里,可能是空的(很遗憾!)。

当“包装”时,可选变量的值是一个包含两个可能值的enum(有点像布尔值)。这个枚举描述了变量是否包含一个值(Some(T)),或者是否包含一个值(None)。

如果有值,则可以通过“展开”变量来获得(从Some(T)中获取T)。

约翰好吗?73号公寓和约翰不一样。公寓= 73号?(转述)

如果你写一个可选变量的名字(例如文本john,不带!),这指的是“包装”enum (Some/None),而不是值本身(T)。所以john不是Person的实例,它没有公寓成员:

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

实际的Person值可以通过多种方式展开:

“强制拆包”:约翰!(如果Person值存在,则给出Person值,如果为nil则出现运行时错误) "optional binding": if let p = John {println(p)}(如果值存在则执行println) "optional chaining": john?.learnAboutSwift()(如果值存在,则执行这个创建的方法)

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

更新:

感叹号也用于声明“隐式未包装的可选选项”的语法中。

在到目前为止的例子中,john变量被声明为var john:Person?,它是可选的。如果需要该变量的实际值,则必须使用上述三种方法之一展开它。

如果它被声明为var john:Person!相反,该变量将是一个隐式未包装的可选变量(参见Apple book中带有此标题的部分)。在访问值时不需要展开这类变量,并且可以在不使用其他语法的情况下使用john。但苹果的书说:

当变量有可能在稍后变为nil时,不应该使用隐式打开的可选选项。如果您需要在变量的生命周期内检查nil值,请始终使用正常的可选类型。

更新2:

Mike Ash的文章“有趣的Swift特性”为可选类型提供了一些动力。我觉得写得很好,很清楚。

更新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.

也许Apple会在未来梳理他们的框架,删除隐式展开参数(“可能永远不会为nil”)的不确定性,并将它们替换为可选的(“当然可能是nil,特别是[希望有文档!)或标准的非可选(“永不为空”)声明,这取决于它们的Objective-C代码的确切行为。

在这种情况下……

John:人!

它意味着,最初John的值为nil,它会被设置,一旦设置,就不会再以nil为先导。因此,为了方便起见,我可以使用更简单的语法来访问可选变量因为这是一个隐式展开的可选变量