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的安全性(在那里很容易忘记处理零案件)。
更新 :
感叹号也用于声明“隐含解包选项”的语法。
在目前的例子中, john
variables被声明为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! UIImage
logo.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? UIImage
logo.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 == nil
testing你的可选值时,它真的返回foo.isNil
,并且当你说foo!
它返回的foo.realObject
的断言foo.isNil == false
。 注意这一点很重要,因为如果你做foo!
时候foo
实际上是零的foo!
,这是一个运行时错误,所以通常你会想使用条件let,除非你确定这个值不会是零。 这种欺骗意味着语言可以被强制types化,而不用强迫你去testing值是否到处都是零。
在实践中,它并不像这样做,因为这个工作是由编译器完成的。 在高层有一个types的Foo?
这与Foo
是分开的,并且阻止了接受Foo
types的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
成员/财产? 要么 - types
person
是否有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
的成员
作为未来讨论为什么使用!
有时不推荐看这个问答