字符串有啥区别?和字符串! (创建可选变量的两种方法)?

Posted

技术标签:

【中文标题】字符串有啥区别?和字符串! (创建可选变量的两种方法)?【英文标题】:What is the difference between String? and String! (two ways of creating an optional variable)?字符串有什么区别?和字符串! (创建可选变量的两种方法)? 【发布时间】:2014-06-06 14:09:43 【问题描述】:

The Swift Programming Language(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

有什么区别,如果根本没有区别,为什么有两种方法?

【问题讨论】:

你应该修正问题的标题 ;) 嗯,这不仅仅是关于字符串。它是关于每个非原始数据类型变量的。我删除了字符串,以便可以将问题作为通用问题。 【参考方案1】:

使用隐式展开的可选项(用 ! 声明)的真正好处与当两个类相互指向并且您需要避免强引用循环时的类初始化有关。例如:

A 类 B 类

A 类的 init 例程需要创建(并拥有)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 被声明为一个隐式展开的可选项(instanceOfB!),我们可以只使用 instanceOfB 来访问它。 (此外,我怀疑编译器也可以对访问进行不同的优化)。

本书第 464 至 466 页给出了一个例子。

总结:

使用 ?如果该值将来可以变为 nil,以便您对此进行测试。 使用!如果它真的不应该在未来变为 nil,但它最初需要为 nil。

【讨论】:

终于有了一个真正回答我的问题而不是简单背书的答案,+1 (offtopic) 为什么 "self.instanceOfB = B(instanceOfA: self)" 必须有 instanceofA 关键字?简单地做“B(self)”不是有效的快速代码吗?这个“init(instanceOfA: A) self.instanceOfA = A ”在我看来也很奇怪。 @Whome instanceOfA 不是关键字,而是我给出的参数名称。默认情况下,所有初始化程序都有必须使用的外部参数名称,因此您不能执行 B(self),除非您已使用 _ 作为外部参数名称声明 B 的 init 函数,例如。 init(_ instanceOfA: A). @tarmes 他可能说因为“self.instanceOfA = A”是编译器错误 - 您将 instanceOfA 设置为类型,而不是参数 (instanceOfA) 我一直在寻找完美的用例,在这里找到了。谢谢@tarmes【参考方案2】:

你应该超越语法糖。

有两种完全不同的多态类型。语法糖只使用其中一种类型。

当你把Foo?写成一个类型时你真的有Optional<Foo>,而当你写Foo!你真的有ImplicitlyUnwrappedOptional<Foo>

这是两种不同的类型,它们也不同于Foo

【讨论】:

【参考方案3】:

您使用? 创建的值是您提到的纯可选值,您应该通过可选绑定(if let unwrappedValue = myOptionalValue)或使用感叹号语法myOptionalValue!.doSomething() 访问它。

您使用! 创建的值称为隐式解包选项。有了它们,您无需在使用它们之前手动打开包装。当你做val myOptionalValue!.doSomething()

当您直接使用myOptionalValue 时,该值将自动为您解包,但请注意这一点,因为当其中实际上没有值时(当它是nil)访问一个隐式解包的值将导致运行时错误。

【讨论】:

【参考方案4】:

? (可选) 表示您的变量可能包含 nil 值,而 ! (unwrapper) 表示您的变量在运行时使用(试图从中获取值)时必须具有内存(或值)。

主要区别在于,当可选项为 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

For more detail, refer a document by Apple Developer Commitee, in detail

【讨论】:

【参考方案5】:

String! 类型称为implicitly unwrapped optional

有时,从程序的结构中可以清楚地看出,在第一次设置该值之后,可选项总是有一个值。在这些情况下,无需在每次访问可选选项时检查和解包它的值,因为可以安全地假定它始终都有一个值。

这些类型的选项被定义为隐式展开的选项。通过在要设为可选的类型之后放置感叹号(字符串!)而不是问号(字符串?)来编写隐式展开的可选。

【讨论】:

【参考方案6】:

在可选链接部分您可以找到答案:

示例类:

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 值。但是,当居住地为 nil 时,此代码总是会触发运行时错误,如上所示。

可选链接提供了一种访问 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."

【讨论】:

使用使用 String 类型的示例代码直接回答问题?【参考方案7】:

上面@tarmes 提到的很好。注意到隐式可选的另一种用法:

假设我有一个可选的Int

let firstInt: Int? = 9

我正在尝试使用可选的模式匹配并像这样使用这个可选的Int

if case let myFirstInt? = firstInt where myFirstInt > 1 
    print("Valid")
 else 
    print("Invalid")

请注意,我正在使用带有本地参数 myFirstInt 的隐式可选,这使得与可选 firstInt 链接的 nil 条件安全。如果现在,我将firstInt 设为nil,它将执行else 条件。相反,如果我对 firstInt 使用 force-unwrap 会导致崩溃,如下所示:

【讨论】:

以上是关于字符串有啥区别?和字符串! (创建可选变量的两种方法)?的主要内容,如果未能解决你的问题,请参考以下文章

java中创建线程的两种方式有啥区别

添加参数的两种情况有啥区别?

选中没有选中的复选框,匹配含有某个字符串的正则,json取值的两种方法,把变量定义在外面跟里面的区别

python中print()内,字符串相加‘,’和‘+’有啥区别?

加入两个 Pyspark 数据帧的两种方法有啥区别

打字稿中数组的两种写作有啥区别