[英]What is the difference between String? and String! (two ways of creating an optional variable)?
在《 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
有什么区别?如果根本没有区别,为什么有2种方法呢?
当两个类相互指向并且您需要避免强引用循环时,使用隐式展开的可选变量(用!声明)的真正好处与类初始化有关。 例如:
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被声明为一个隐式未包装的可选(instanceOfB!),我们可以仅使用instanceOfB对其进行访问。 (此外,我怀疑编译器也可以不同地优化访问)。
这本书的第464至466页给出了一个示例。
您应该超越语法糖。
有两种完全不同的多态类型。 语法糖仅使用这些类型中的一种或另一种。
当您写Foo?
作为类型,您实际上具有Optional<Foo>
,而在编写Foo!
您确实有ImplicitlyUnwrappedOptional<Foo>
。
这是两种不同的类型,它们也与Foo
不同。
String!
kind称为隐式展开的可选 :
有时,从程序的结构中可以明显看出,在首次设置可选值之后,该可选值将始终具有一个值。 在这些情况下,删除每次访问可选参数的值的必要性很有用,因为可以安全地假定它始终具有一个值。
这些类型的可选定义为隐式解包的可选。 您通过在要使其为可选的类型之后放置感叹号(String!)而不是问号(String?)来编写隐式解包的可选。
您使用?
创建的值?
是您提到的普通可选值,您应该通过可选绑定( if let unwrappedValue = myOptionalValue
)或使用感叹号语法myOptionalValue!.doSomething()
。
使用创建的值!
被称为隐式展开的可选。 使用它们,您无需在使用它们之前手动打开包装。 当您执行val myOptionalValue!.doSomething()
。
当您直接使用myOptionalValue
时,该值将自动为您解包,但是请谨慎操作,因为在其中实际上没有任何值(当值为nil
)时访问隐式解包的值将导致运行时错误。
? (可选)表示您的变量可能包含nil值,而
! (解包器)表示在运行时使用变量(尝试从中获取值)时,变量必须具有内存(或值)。
主要区别在于,当可选值为nil时,可选链接会正常失败,而当可选值为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
在可选的链接部分中,您会找到答案:
示例类:
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
如果尝试访问此人的住所的numberOfRooms属性,方法是在住所后放置一个感叹号以强制其值解开,从而触发运行时错误,因为没有要解开的住所值:
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
上面的代码在john.residence具有非nil值时成功,并将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."
上面@tarmes提到的很好。 注意了隐式可选的另一种用法:
可以说我有一个可选的Int
:
let firstInt: Int? = 9
我正在尝试使用可选的模式匹配,并像这样使用此可选的Int
:
if case let myFirstInt? = firstInt where myFirstInt > 1 {
print("Valid")
} else {
print("Invalid")
}
请注意,我正在使用带有局部参数myFirstInt
隐式optional,使它对于与可选firstInt
链接的nil
条件是安全的。 如果现在,我将firstInt
设为nil
,它将执行else条件。 相反,如果我对firstInt
使用force- firstInt
会导致崩溃,则如下所示:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.