在 Swift编程语言(Apple的书)中,我读过您可以通过两种方式创建可选变量:使用问号(?)或使用感叹号( !)。
不同之处在于,当您使用(?)获得可选值时,每次需要值时都必须使用感叹号:
var str: String? = "Question mark?"
println(str!) // Exclamation mark needed
str = nil
使用(!)时,您可以在没有后缀的情况下获得它:
var str: String! = "Exclamation mark!"
println(str) // No suffix needed
str = nil
有什么区别,如果没有任何区别,为什么会有两种方式?
答案 0 :(得分:59)
使用隐式展开的选项(使用!声明)的真正好处与类初始化有关,当两个类相互指向并且您需要避免强引用循环时。例如:
A类< - > B级
A类的初始化程序需要创建(并拥有)B类,B需要弱引用回A:
class A {
let instanceOfB: B!
init() {
self.instanceOfB = B(instanceOfA: self)
}
}
class B {
unowned let instanceOfA: A
init(instanceOfA: A) {
self.instanceOfA = instanceOfA
}
}
现在,
self
传递给B类的初始化。instanceOfB
必须是可选的。但是,一旦创建了A,使用instanceOfB访问instanceOfB会很烦人!因为我们知道必须有一个B
为了避免这种情况,instanceOfB被声明为implicity unwrapped可选(instanceOfB!),我们可以使用instanceOfB来访问它。 (此外,我怀疑编译器也可以不同地优化访问)。
本书第464至466页给出了一个例子。
答案 1 :(得分:7)
你应该超越语法糖。
有两种完全不同的多态类型。语法糖只使用这些类型中的一种或另一种。
当您将Foo?
作为一种类型Optional<Foo>
时,当您撰写Foo!
时,您确实拥有ImplicitlyUnwrappedOptional<Foo>
。
这是两种不同的类型,它们也与Foo
不同。
答案 2 :(得分:2)
String!
种称为implicitly unwrapped optional:
有时从程序的结构中可以清楚地看出,在首次设置该值之后,可选项将始终具有值。在这些情况下,每次访问时都不需要检查和解包可选的值,因为可以安全地假设它始终具有值。
这些类型的期权被定义为隐式解包的期权。您通过在要使其成为可选的类型之后放置感叹号(String!)而不是问号(String?)来编写隐式展开的可选项。
答案 3 :(得分:2)
您使用?
创建的值是您提到的普通可选值,您应该通过可选绑定(if let unwrappedValue = myOptionalValue
)或使用感叹号语法myOptionalValue!.doSomething()
来访问它。
使用!
创建的值称为隐式解包的选项。有了它们,您在使用它们之前不需要手动打开包装。当你val myOptionalValue!.doSomething()
时。
当您直接使用myOptionalValue
时,该值将自动解包,但请注意这一点,因为当实际上没有值时访问隐式展开的值(当它是nil
时)将导致运行时错误。
答案 4 :(得分:2)
<强>? (可选)表示您的变量可能包含零值,而! (unwrapper)表示你的变量在运行时使用(尝试从中获取值)时必须有一个内存(或值)。
主要区别在于,当optional是nil时,可选链接正常失败,而当optional是nil时,强制解包会触发运行时错误。
为了反映可以在nil值上调用可选链接的事实,可选链接调用的结果始终是可选值,即使您要查询的属性,方法或下标返回非可选值。您可以使用此可选返回值来检查可选链接调用是否成功(返回的可选项包含值),或者由于链中的nil值(返回的可选值为nil)而未成功。
具体来说,可选链接调用的结果与预期返回值的类型相同,但包含在可选中。通过可选链接访问时,通常返回Int的属性将返回 Int?。
var defaultNil : String? // declared variable with default nil value
println(defaultNil) >> nil
var canBeNil : String? = "test"
println(canBeNil) >> optional(test)
canBeNil = nil
println(canBeNil) >> nil
println(canBeNil!) >> // Here nil optional variable is being unwrapped using ! mark (symbol), that will show runtime error. Because a nil optional is being tried to get value using unwrapper
var canNotBeNil : String! = "test"
print(canNotBeNil) >> "test"
var cantBeNil : String = "test"
cantBeNil = nil // can't do this as it's not optional and show a compile time error
For more detail, refer a document by Apple Developer Commitee, in detail
答案 5 :(得分:1)
在可选链接部分,您可以找到答案:
示例类:
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
如果您尝试访问此人住所的numberOfRooms属性,通过在居住后放置一个感叹号来强制展开其值,则会触发运行时错误,因为没有驻留值来解包:
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
当john.residence具有非零值并且将roomCount设置为包含适当房间数的Int值时,上面的代码成功。但是,如上所示,当驻留为零时,此代码始终会触发运行时错误。
可选链接提供了另一种访问numberOfRooms值的方法。要使用可选链接,请使用问号代替感叹号:
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
答案 6 :(得分:1)
@tarmes上面提到的很好。注意隐式可选的另一种用法:
假设我有一个可选的Int
:
let firstInt: Int? = 9
我正在尝试使用可选的模式匹配,并像这样使用这个可选的Int
:
if case let myFirstInt? = firstInt where myFirstInt > 1 {
print("Valid")
} else {
print("Invalid")
}
请注意,我在本地参数myFirstInt
中使用隐式可选项,这使得与nil
条件链接的firstInt
条件安全无误。如果现在,我将firstInt
设为nil
,它将执行其他条件。相反,如果我使用firstInt
强制解包会导致崩溃,那就像这样: