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”,因为我最初的想法。我只是误解了《人与人》是完全不同的类型。一旦我明白了,所有其他的?,!疯狂,以及下面这些精彩的回答,让我觉得更有道理。


当前回答

问问你自己

这种类型的人吗?有公寓会员/房产?或 这种类型的人有公寓成员/房产吗?

如果你不能回答这个问题,那么继续阅读:

要理解,你可能需要对泛型有超基础的理解。在这里看到的。Swift中的很多东西都是用泛型编写的。可选的包括

下面的代码可以从斯坦福大学的视频中获得。强烈推荐你看前5分钟

“Optional”为enum,只有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的属性?你看到它了吗?根本就不在那儿!然而,如果你打开它ie做人!然后你就可以……它在引擎盖下面做的是:Optional<Person>。一些(人(名字:“John Appleseed”))


你是否定义了var john: Person而不是:var john: Person?那么你就不再需要了!使用,因为Person本身有一个apartment的成员


作为以后讨论为什么使用!打开包装有时不建议看这个问答

其他回答

John是一个可选的变量,它可以包含一个nil值。要确保该值不是nil,请使用a !在var名称的末尾。

从文档

一旦您确定可选选项确实包含一个值,您可以通过在可选选项名称的末尾添加一个感叹号(!)来访问它的底层值。感叹号有效地表示,“我知道这个可选选项肯定有价值;请使用它。”

另一种检查非nil值的方法是(可选的展开)

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

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

Person? thisPerson;

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

thisPerson.Value
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

因为谷歌员工:

john!.department

...告诉编译器:

我知道约翰可有可无 把它当成有价值的东西来使用 如果没有,就崩溃

在生产中,使用保护let或if let来处理无值和无效硬崩溃的情况。

一些大局观的观点可以补充到其他有用但更注重细节的答案中:

在Swift中,感叹号出现在几种情况下:

强制展开:let name = nameLabel!.text 隐式打开可选项:var logo: UIImageView! 强制铸造:标志。图像=事物!用户界面图像 未处理的异常:试试!NSJSONSerialization。JSONObjectWithData(数据,[])

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

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

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

并不是所有有效的代码都符合Swift的编译时类型系统,也不是任何语言的静态类型检查。有些情况下,你可以从逻辑上证明错误永远不会发生,但你不能向编译器证明它。这就是为什么Swift的设计师一开始就添加了这些功能。

然而,无论何时使用!,您都排除了为错误提供恢复路径,这意味着……

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

感叹号还表示,“嘿,斯威夫特,我非常确定这个错误永远不会发生,所以你最好让我的整个应用程序崩溃,而不是让我为它编写恢复路径。”

这是一个危险的论断。它可能是正确的:在任务关键型代码中,您已经认真考虑了代码的不变量,伪输出可能比崩溃更糟糕。

然而,当我看到!在野外,很少有人如此谨慎地使用它。相反,它通常意味着,“这个值是可选的,我并没有真正认真思考为什么它可以为nil或如何正确处理这种情况,但添加!让它编译……所以我的代码是正确的,对吧?”

小心感叹号的傲慢。而不是……

3.感叹号最好少用。

每一个!构念有?强制你处理错误/nil情况的对应项:

条件解包装:if let name = nameLabel?文本{…} 可选:var logo: UIImageView? 条件强制转换:标志。Image = thing as?用户界面图像 失败时为空异常:试试?NSJSONSerialization。JSONObjectWithData(数据,[])

如果你想要使用!,最好仔细考虑一下为什么你不使用!代替。使程序崩溃真的是最好的选择吗?操作失败?为什么这个值是可选的/可失败的?

是否有一个合理的恢复路径,你的代码可以采取在nil/错误的情况下?如果是,编码它。

如果它不可能是nil,如果错误永远不会发生,那么有没有一种合理的方法来重做你的逻辑,让编译器知道?如果是,那就去做;您的代码将更不容易出错。

有时没有合理的方法来处理错误,简单地忽略错误——从而处理错误的数据——会比崩溃更糟糕。这些时候就应该使用强制拆包。

我定期搜索我的整个代码库!并审查它的每一次使用。很少有使用方法经得起推敲。(在撰写本文时,整个Siesta框架只有两个实例。)

这并不是说你不应该使用!在你的代码中——只是你应该谨慎地使用它,永远不要让它成为默认选项。