String之间有什么区别?和字符串! (创建可选变量的两种方法)?

时间:2014-06-06 14:09:43

标签: ios swift

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

有什么区别,如果没有任何区别,为什么会有两种方式?

7 个答案:

答案 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
    }
}

现在,

  • B类需要引用A类进行初始化。
  • A类只有在完全初始化后才能将self传递给B类的初始化。
  • 对于在创建B类之前被视为初始化的A类,因此属性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强制解包会导致崩溃,那就像这样:

enter image description here