Swift 中的“未包装价值”是什么?

我正在学习 iOS 8/OSX 10.10中的 Swift,通过跟随 本教程,术语“ 未包装的价值”被多次使用,就像在这一段(在 对象和类下) :

当使用可选值时,可以在操作之前编写 类似的方法、属性和下标? 是 nil,则忽略? 之后的所有内容以及整体的值 表达式为 null。否则,取消可选值和 在 未包装的价值上的? 行为之后的一切。在这两种情况下, 整个表达式的值是一个可选值。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

我不明白,在网上搜索,没有运气。

这是什么意思?


剪辑

从 Cezary 的回答来看,原始代码的输出和最终解决方案(在操场上测试)之间存在细微的差别:

原始代码

Original code

Cezary 的解决方案

Cezary's solution

在第二种情况下,超类的属性显示在输出中,而在第一种情况下是一个空对象。

两种情况下的结果不应该是一样的吗?

相关问答: Swift 中的可选值是什么?

85729 次浏览

首先,你必须理解什么是可选类型。可选类型基本上意味着变量 可以是nil

例如:

var canBeNil : Int? = 4
canBeNil = nil

问号表示 canBeNil可以是 nil

这是行不通的:

var cantBeNil : Int = 4
cantBeNil = nil // can't do this

如果变量是可选的,那么必须使用 打开它才能从变量中获取值。这只意味着在最后放一个叹号。

var canBeNil : Int? = 4
println(canBeNil!)

您的代码应该如下所示:

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare!.sideLength

旁注:

你也可以使用叹号代替问号来声明可选项来自动展开。

例如:

var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed

因此,修复代码的另一种方法是:

let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare.sideLength

编辑:

您看到的差异正是可选值为 包好了这一事实的症状。上面还有一层。解开包装版本只显示直接对象,因为它是未包装的。

一个快速的操场比较:

playground

在第一种和第二种情况下,对象没有被自动展开,所以您看到两个“层”(\{\{...}}) ,而在第三种情况下,您只看到一个层({...}) ,因为对象被自动展开。

第一种情况和第二种情况的区别在于,如果将 optionalSquare设置为 nil,那么第二种情况将给出一个运行时错误。使用第一种情况下的语法,您可以执行以下操作:

if let sideLength = optionalSquare?.sideLength {
println("sideLength is not nil")
} else {
println("sidelength is nil")
}

现有的正确答案是伟大的,但我发现,为了充分理解这一点,我需要一个很好的类比,因为这是一个非常抽象和奇怪的概念。

因此,让我来帮助那些“右脑”(视觉思维)开发人员,除了给出正确的答案之外,还要给出一个不同的视角。这里有一个很好的类比,对我很有帮助。

生日礼物包装类比

可选项就像生日礼物一样,有着硬硬的彩色包装。

你不知道包装里面是否有东西,直到你打开礼物ーー也许里面什么都没有!如果里面有东西,可能是另一个礼物,也是包装的,也是 可能什么都没有。您甚至可能打开100个嵌套的礼物,最终发现有 除了包装什么都没有

如果可选的值不是 nil,那么现在您已经显示了一个包含 什么东西的框。但是,特别是如果值不是显式类型的,并且是一个变量而不是一个预定义的常量,那么您可能仍然需要 打开盒子,然后才能知道框中的具体内容,比如 什么类型,或者实际的 价值是什么。

盒子里是什么? 类比

即使你打开了变量,你仍然像在 SE7EN 的最后一幕中的布拉德 · 皮特(警告: 剧透和非常 R 级的粗俗语言和暴力) ,因为即使你打开了现在,你仍然处于以下情况: 现在有了 nil,或者一个包含 < em > something (但是不知道是什么)的框。

你可能知道 什么东西的类型。例如,如果您将变量声明为类型 [Int:Any?],那么您就会知道您有一个(可能是空的)字典,其中带有整数下标,可以生成任何旧类型的包装内容。

这就是为什么在 Swift 中处理集合类型(字典和数组)会有些棘手。

举个例子:

typealias presentType = [Int:Any?]


func wrap(i:Int, gift:Any?) -> presentType? {
if(i != 0) {
let box : presentType = [i:wrap(i-1,gift:gift)]
return box
}
else {
let box = [i:gift]
return box
}
}


func getGift() -> String? {
return "foobar"
}


let f00 = wrap(10,gift:getGift())


//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.


var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])


//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is


let asdf : String = b4r!! as! String


print(asdf)

’?’表示可选的链式表达式

“ !”的意思是力值
enter image description here

Swift 高度重视类型安全。整个斯威夫特语言的设计都考虑到了安全问题。这是斯威夫特的标志之一,也是你应该张开双臂欢迎的标志之一。它将有助于开发干净、可读的代码,并有助于防止应用程序崩溃。

Swift 中的所有选项都用 ?符号划分。通过在声明为可选类型的类型名称后面设置 ?,您实际上是 选角,这不是作为 ?之前的类型,而是作为 可以选择类型。


注意: 变量或类型 Int没有,与 Int?相同。它们是两种不同的类型,不能互相操作。


使用可选项

var myString: String?


myString = "foobar"

这确实意味着您使用的是 String类型。这意味着您使用的是 String?类型(可选字符串或可选字符串)。事实上,只要你想

print(myString)

在运行时,调试控制台将打印 Optional("foobar")。“ Optional()”部分指出这个变量在运行时可能有也可能没有值,但是当前正好包含字符串“ foobar”。这个“ Optional()”指示将保留,除非您执行所谓的“取消包装”可选值。

取消包装 一个可选类型意味着您现在将该类型强制转换为非可选类型。这将生成一个新类型,并将驻留在该可选类型中的值分配给新的非可选类型。通过这种方式,您可以对该变量执行操作,因为编译器已经保证该变量具有可靠的值。


有条件地取消包装 将检查可选项中的值是否为 nil。如果它不是 nil,那么将会有一个新创建的常量变量,它将被赋值并且 解开包装被赋给非可选常量。从那里您可以安全地使用非可选的 if块。

注意: 您可以将您的条件取消包装常量与您正在取消包装的可选变量同名。

if let myString = myString {
print(myString)
// will print "foobar"
}

有条件地展开选项是访问选项值的最干净的方法,因为如果它包含一个 nil 值,那么 if let 块中的所有内容都将不会执行。当然,像任何 if 语句一样,您可以包含 else 块

if let myString = myString {
print(myString)
// will print "foobar"
}
else {
print("No value")
}

强制解包 是通过使用所谓的 !(“ bang”)运算符来完成的。这样虽然不太安全,但仍然允许编译代码。但是,无论何时使用 bang 操作符,在强制展开之前,必须1000% 确定变量实际上包含一个实数值。

var myString: String?


myString = "foobar"


print(myString!)

上述代码是完全有效的 Swift 代码。它打印出设置为“ foobar”的 myString值。用户将看到 foobar在控制台中打印出来,就是这样。但让我们假设这个值从未设置过:

var myString: String?


print(myString!)

现在情况不同了。与 Objective-C 不同的是,每当尝试强制打开一个可选项时,如果没有设置可选项,那么当您尝试打开可选项以查看应用程序内部的内容时,就会崩溃。


拆开 W 型铸件。正如我们前面所说的,当您是 unwrapping时,您实际上是在将非可选类型强制转换为非可选类型,您也可以将非可选类型强制转换为其他类型。例如:

var something: Any?

在我们的代码中的某个地方,变量 something将被设置为某个值。也许我们正在使用泛型,或者也许有一些其他的逻辑正在发生,这将导致这个改变。因此,在后面的代码中,我们希望使用 something,但是如果它是不同的类型,我们仍然可以对它进行不同的处理。在这种情况下,您需要使用 as关键字来确定:

注意: as操作符是您在 Swift 中输入 cast 的方式。

// Conditionally
if let thing = something as? Int {
print(thing) // 0
}


// Optionally
let thing = something as? Int
print(thing) // Optional(0)


// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised

注意两个 as关键字之间的区别。像以前一样,当我们强制打开一个可选项时,我们使用 ! bang 操作符来做到这一点。在这里,您将做同样的事情,但不仅仅是一个非可选的铸造,你也铸造它作为 Int。而且它 必须的能够向下转换为 Int,否则,就像当值为 nil时使用 bang 操作符一样,您的应用程序将崩溃。

为了在某种排序或数学运算中使用这些变量,必须对它们进行解包装。

例如,在 Swift 中,只有相同类型的有效数字数据类型可以相互操作。当您使用 as!强制转换类型时,您将强制向下转换该变量,就像您是 某些一样,因此操作该类型是安全的,不会使应用程序崩溃。只要这个变量确实属于您要转换它的类型,那么就没问题,否则您就会遇到麻烦。

尽管如此,使用 as!进行强制转换将允许编译代码。通过与 as?选角是一个不同的故事。事实上,as?Int声明为完全不同的数据类型。

现在是 Optional(0)

如果你想做作业的话

1 + Optional(1) = 2

你的数学老师可能会给你一个“ F”。斯威夫特也是。只不过斯威夫特宁愿根本不编译也不给你打分。因为到最后,可选项实际上可能是零.

安全第一的孩子。

在 Swift 中有7种方法可以展开可选值。

  1. 可选绑定-安全
  2. 可选链接-安全
  3. 可选模式-安全
  4. 无聚合-安全
  5. 警卫语句-安全
  6. 强制拆封-不安全
  7. 隐式拆封-不安全