Swift语言中的感叹号是什么意思?
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语言很陌生。 只是试图让基础知识。
  更新: 
 我错过的一大难题(不是在答案中直接说明 – 至less在写这篇文章的时候没有)是当你做下面的事时: 
 var john: Person? 
 这并不意味着“ john是人的types,可能是零”,就像我原先想的那样。 我只是误解了Person? 是完全独立的types。 一旦我掌握了那个,其他的?  , ! 疯狂,以及下面的伟大答案,更有意义。 
这是什么意思“解开实例”? 为什么有必要?
据我所知(这对我来说也是新鲜事)…
“包装”一词意味着我们应该把一个可选variables当作一个用闪闪发亮的纸包裹的礼物,可能(可惜)是空的 。
 当“包装”时,可选variables的值是一个具有两个可能值的枚举(有点像布尔值)。 这个枚举描述variables是否保存一个值( Some(T) ),或者不保存( None )。 
 如果有值,可以通过“展开”variables(从Some(T)获得T来获得。 
john!.apartment = number73与john.apartment = number73什么不同? (意译)
 如果你写了一个可选variables的名字(比如说文本john ,没有! ),那么它就是指“包装的”枚举(Some / None),而不是值本身(T)。 所以john不是人的一个实例,也没有apartment成员: 
 john.apartment // 'Person?' does not have a member named 'apartment' 
 实际的Person值可以用各种方式解开: 
-   “强行解开”: john!(如果存在,则给出Person值,如果为零,则给出运行时错误)
-   “可选绑定”: if let p = john { println(p) }(如果值存在,则执行println)
-   “可选链接”: john?.learnAboutSwift()(如果值存在,则执行此john?.learnAboutSwift()方法)
我想你可以select其中一种解开方式,这取决于在这种情况下会发生什么,以及这种情况的可能性。 这种语言devise迫使零案件被明确处理,我认为这提高了对Obj-C的安全性(在那里很容易忘记处理零案件)。
更新 :
感叹号也用于声明“隐含解包选项”的语法。
 在目前的例子中, johnvariables被声明为var john:Person?  ,它是一个可选的。 如果您想要该variables的实际值,则必须使用以上三种方法之一来展开它。 
 如果它被声明为var john:Person! 相反,variables将是一个隐式解包可选(请参阅苹果书中的标题部分)。 访问该值时不需要展开这种types的variables,并且可以使用john而无需额外的语法。 但是苹果的书说: 
在稍后可能variables为零的情况下,不应该使用隐式解包的option。 如果您需要在variables的生命周期中检查nil值,请始终使用正常的可选types。
更新2 :
Mike Ash的文章“ 有趣的Swift特性 ”为可选types提供了一些动机。 我认为这是伟大的,明确的写作。
更新3 :
 另一个有用的文章,关于感叹号的隐式解包可选用法:由Chris Adamson“ Swift and the Last Mile ”。 文章解释说,这是苹果用来声明Objective-C框架所使用的可能包含零的types的一个实际措施。 将types声明为可选(使用? )或隐式解包(使用! )是“安全和便利之间的折衷”。 在文章中给出的例子中,苹果已经select将这些types声明为隐式解包,使得调用代码更加方便,但是不太安全。 
也许苹果可能会在未来梳理他们的框架,消除隐含解包(“可能永不为零”)参数的不确定性,并用可选的(“当然可能为零(特别是希望logging的情况下)”或者标准非 – 基于其Objective-C代码的确切行为,可选(“从不为零”)声明。
以下是我认为的区别:
 var john: Person? 
手段约翰可以是零
 john?.apartment = number73 
编译器会将此行解释为:
 if john != nil { john.apartment = number73 } 
而
 john!.apartment = number73 
编译器将会简单地解释这一行:
 john.apartment = number73 
因此,使用! 将打开if语句,并使其运行得更快,但如果john为零,则会发生运行时错误。
所以在这里包装并不意味着它是内存包装,但它意味着它是代码包装,在这种情况下,它是用一个if语句包装,因为苹果在运行时密切关注性能,他们想给你的方式让您的应用程序以最佳性能运行。
TL; DR
Swift语言中的感叹号是什么意思?
感叹号有效地说:“我知道这个可选肯定有价值, 请使用它“。这被称为强制展开可选的值:
例
 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
如果john是一个可选的var(这样声明)
 var john: Person? 
那么约翰就没有价值(按照ObjC的说法,零值)
感叹号基本上告诉编译器“我知道这有一个价值,你不需要testing它”。 如果你不想使用它,你可以有条件地testing它:
 if let otherPerson = john { otherPerson.apartment = number73 } 
这个内部只会评估约翰是否有价值。
  john是一个可选的var 。 所以可以包含一个nil值。 确保价值不是零利用! 在var名称的末尾。 
从文档
一旦确定可选项包含一个值,可以通过在可选名称的末尾添加一个感叹号(!)来访问其基础值。 感叹号有效地说:“我知道这个可选肯定有价值, 请使用它“。
另一种检查非零值的方法是
  if let j = json { // do something with j } 
一些大的图片视angular添加到其他有用的,但更详细为中心的答案:
在Swift中,感叹号出现在以下几个方面:
-  强制解包: let name = nameLabel!.text
-  隐式解开的optionals: var logo: UIImageView!
-  强制铸造: logo.image = thing as! UIImagelogo.image = thing as! UIImage
-  未处理的exception: try! NSJSONSerialization.JSONObjectWithData(data, [])try! NSJSONSerialization.JSONObjectWithData(data, [])
它们中的每一个都是具有不同含义的不同语言结构,但它们都有三个重要的共同点:
感叹号绕过了Swift的编译时安全检查。
 当你使用! 在Swift中,你基本上是在说:“嘿,编译器,我知道你认为在这里会发生一个错误,但是我完全确定它永远不会知道 。 
并非所有有效的代码都适合Swift的编译时types系统 – 或者任何语言的静态types检查。 在某些情况下,你可以从逻辑上certificate一个错误永远不会发生,但是你不能向编译器certificate它。 这就是为什么Swift的devise师首先添加了这些function。
 但是,只要你使用!  ,你排除了一个错误的恢复path,这意味着… 
感叹号是潜在的崩溃。
一个惊叹号还说,“嘿斯威夫特,我很确定,这个错误不会发生,这是更好的让你崩溃我的整个应用程序,而不是我编写一个恢复path。”
这是一个危险的说法。 它可以是正确的:在关键代码中,你认真考虑了代码的不variables,可能是假输出比崩溃更糟糕。
 但是,当我看到! 在野外,很less使用它。 相反,它通常意味着,“这个价值是可选的,我真的没有想太多,为什么它可能是零或什么约,但增加! 使它编译…所以我的代码是好的,对不对? 
谨防感叹号的傲慢。 代替…
感叹号最好使用,几乎从不使用。
 每一个! 构造有一个? 对方强迫你处理错误/无情况: 
-  有条件解包: if let name = nameLabel?.text { ... }
-  可选项: var logo: UIImageView?
-  有条件的转换: logo.image = thing as? UIImagelogo.image = thing as? UIImage
-  无故障例外: try? NSJSONSerialization.JSONObjectWithData(data, [])try? NSJSONSerialization.JSONObjectWithData(data, [])
 如果你想使用!  ,仔细考虑你为什么不使用它总是好的? 代替。 如果你的程序崩溃真的是最好的select! 操作失败? 有没有一个合理的恢复path你的代码可以采取在零/错误的情况下? 如果是这样,编码。 
 我定期search我的整个代码库! 并审核其每一次使用。 很less有用法经过审查。  (在撰写本文时,整个Siesta框架恰好有两个 实例 。) 
 这并不是说你永远不要使用! 在你的代码中 – 只是你应该经常使用它,并且永远不要使它成为默认选项。 
这里有些例子:
 var name:String = "Hello World" var word:String? 
 其中word是一个可选值。 意味着它可能包含或不包含一个值。 
 word = name 
 这里name有一个值,所以我们可以分配它 
 var cow:String = nil var dog:String! 
  dog被强行打开的地方意味着它必须包含一个值 
 dog = cow 
 应用程序将崩溃,因为我们分配nil来解包 
在这种情况下…
var约翰:人!
这意味着,最初约翰将没有价值,它将被设定,一旦设定,将永远不会再被领导。 因此,为了方便起见,我可以使用更简单的语法来访问可选的var,因为这是一个“Implicitly unwrapped optional”
如果你来自C族语言,你会想到“指向Xtypes对象的指针,它可能是内存地址0(NULL)”,如果你来自dynamictypes语言,你将会思考“可能是typesX但可能是未定义types的对象”。 这两者其实都不是正确的,尽pipe迂回的第一个接近。
你应该考虑的方式就好像它是一个对象一样:
 struct Optional<T> { var isNil:Boolean var realObject:T } 
 当你用foo == niltesting你的可选值时,它真的返回foo.isNil ,并且当你说foo! 它返回的foo.realObject的断言foo.isNil == false 。 注意这一点很重要,因为如果你做foo!时候foo实际上是零的foo!  ,这是一个运行时错误,所以通常你会想使用条件let,除非你确定这个值不会是零。 这种欺骗意味着语言可以被强制types化,而不用强迫你去testing值是否到处都是零。 
 在实践中,它并不像这样做,因为这个工作是由编译器完成的。 在高层有一个types的Foo? 这与Foo是分开的,并且阻止了接受Footypes的Foo接收一个nil值,但是在低级别,一个可选值不是一个真正的对象,因为它没有属性或者方法。 实际上它可能是一个指针,它可以通过NULL(0)和相应的testing进行解压。 
在其他情况下,您会看到一个感叹号,如下所示:
 func foo(bar: String!) { print(bar) } 
这大致相当于接受一个可选的强制解包,即:
 func foo(bar: String?) { print(bar!) } 
你可以使用这个方法在技术上接受一个可选的值,但是如果它是零,将会有一个运行时错误。 在当前版本的Swift中,这显然会绕过is-not-nil断言,所以你将会有一个低级的错误。 一般来说不是一个好主意,但是当从另一种语言转换代码时它可能是有用的。
在目标C中,没有值的variables等于'nil'(也可以使用与'0'相同的'nil'值和false),因此可以在条件语句中使用variables(variables的值与'TRUE '和没有值的那些等于'FALSE')。
Swift通过提供“可选值”来提供types安全性。 即防止分配不同types的variables而形成的错误。
所以在Swift中,只有条件语句可以提供布尔值。
 var hw = "Hello World" 
在这里,虽然'hw'是一个string,但是它不能用在像if中的if语句中。
 //This is an error if hw {..} 
为此,需要将其创build为,
 var nhw : String? = "Hello World" //This is correct if nhw {..} 
那! 意味着你是强制解开对象的! 如下。 更多信息可以在苹果文档中find,可以在这里find: https : //developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html
如果您熟悉C#,那么这就像使用问号声明的可空types:
 Person? thisPerson; 
在这种情况下,感叹号等同于访问可空types的.Value属性,如下所示:
 thisPerson.Value 
那! 在一个对象的末尾说对象是一个可选的,如果它可以返回一个零。 这通常用于捕获会导致程序崩溃的错误。
简而言之(!):你已经声明了一个variables,并且你确定variables正在保存一个值。
 let assumedString: String! = "Some message..." let implicitString: String = assumedString 
否则你将不得不在每一个传递价值之后做到这一点…
 let possibleString: String? = "An optional string." let forcedString: String = possibleString! // requires an exclamation mark 
约翰是一个可选的人,这意味着它可以持有一个价值或零。
 john.apartment = number73 
如果john不是可选的,则使用它。 既然约翰从来不是零,我们可以肯定它不会以无价值的方式给公寓打电话。 而
 john!.apartment = number73 
承诺编译器,约翰不是零,然后解开可选获得约翰的价值,并访问约翰的公寓属性。 如果你知道约翰不是零,请使用这个。 如果你调用这个nil可选,你会得到一个运行时错误。
该文档包括一个很好的例子,用于转换的数字是可选的。
 if convertedNumber { println("\(possibleNumber) has an integer value of \(convertedNumber!)") } else { println("\(possibleNumber) could not be converted to an integer") } 
简单地说,感叹号意味着一个可选的正在解包。 一个可选的是一个可以有或没有值的variables – 所以你可以检查variables是否为空,使用如此处所示的if let语句,然后强制解包。 如果你强制展开一个空的可选项,你的程序将会崩溃,所以要小心! 通过在明确赋值给variables的末尾加一个问号来声明可选项,例如我可以这样写:
 var optionalExample: String? 
这个variables没有值。 如果我打开它,程序会崩溃,Xcode会告诉你,你试图打开一个值为零的可选项。
希望有所帮助。
简单的话
使用感叹号表示variables必须包含非零值(它永远不会为零)
整个故事从一个叫做可选variables的特征开始。 这些是可能具有价值或可能没有价值的变数。 一般来说,swift不允许我们使用一个未初始化的variables,因为这可能会导致崩溃或意外的原因,并为后门服务器占位符。 因此,为了声明一个最初不确定的variables,我们使用'?'。 当声明这样一个variables时,要将它用作某个expression式的一部分,在使用之前必须解开它们,解包是一个操作,通过该操作发现variables的值,这适用于对象。 如果您尝试使用它们,则不会解包,您将有编译时错误。 要打开一个variables,它是一个可选的variables,感叹号“!” 用来。
现在有些时候,你知道这样的可选variables将被例如系统或你自己的程序赋值,但是在某些时候,例如UI出口,在这种情况下,而不是使用问号“?”来声明可选variables。 我们用 ”!”。
因此系统知道这个variables是用“!”声明的 现在是可选的,没有任何价值,但会在其后的一生中获得价值。
因此感叹号包含两种不同的用法:1.声明一个variables,它将是可选的,并且将在以后得到值2.在一个expression式中使用它之前解开一个可选variables。
以上描述避免了太多的技术性的东西,我希望。
如果您将其作为可选项使用,则会打开可选项并查看是否有任何内容。 如果你在if-else语句中使用它,那么代码是NOT。 例如,
 if (myNumber != 3){ // if myNumber is NOT 3 do whatever is inside these brackets. ) 
一个可选的variables可能包含一个值,或者可能不是
 情况1: var myVar:String? = "Something" var myVar:String? = "Something" 
 情况2: var myVar:String? = nil var myVar:String? = nil 
 现在,如果你问myVar !,你告诉编译器返回一个值的情况下,它会返回"Something" 
在情况2它会崩溃。
含义! 标记将强制编译器返回一个值,即使它不在那里。 那为什么这个名字强制解包 。
 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 
问你自己
-  types的person?有apartment成员/财产? 要么
-  typesperson是否有apartment成员/财产?
如果您不能回答这个问题,请继续阅读:
要了解您可能需要对generics有超级基本的理解。 看到这里 。 Swift中的很多东西都是使用generics编写的。 包括可选项目
下面的代码已经可以从这个斯坦福的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 属性 ? 你有看到它吗?  根本就没有 ! 但是,如果你打开它,即person! 那么你可以…它下面做的是: Optional<Person>.Some(Person(name: "John Appleseed")) 
 你是否定义了var john: Person而不是: var john: Person? 那么你将不再需要拥有! 使用,因为Person本身确实有一个apartment的成员 
 作为未来讨论为什么使用! 有时不推荐看这个问答