我正在按照 this tutorial 学习适用于 iOS 8 / OSX 10.10 的 Swift,并且多次使用术语“unwrapped value”,如本段所示(在 Objects and Class em>):
使用可选值时,您可以编写 ?在方法、属性和下标等操作之前。如果 ?是零,之后的一切?被忽略,整个表达式的值为 nil。否则,可选值将被解包,以及 ? 之后的所有内容。作用于展开的值。在这两种情况下,整个表达式的值都是可选值。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
我不明白,在网上搜索没有运气。
这是什么意思?
编辑
从 Cezary 的回答来看,原始代码的输出与最终解决方案(在操场上测试)之间存在细微差别:
原始代码
https://i.stack.imgur.com/Ns3my.png
Cezary的解决方案
https://i.stack.imgur.com/RsvFp.png
在第二种情况下,超类的属性显示在输出中,而在第一种情况下有一个空对象。
两种情况下的结果不应该是相同的吗?
相关问答:What is an optional value in Swift?
首先,您必须了解什么是 Optional 类型。可选类型基本上意味着变量可以是 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
编辑:
您看到的差异正是可选值被包装这一事实的症状。上面还有一层。展开的版本只显示直线对象,因为它是展开的。
一个快速的游乐场比较:
https://i.stack.imgur.com/ClB78.png
在第一种和第二种情况下,对象不会自动展开,因此您会看到两个“层”({{...}}
),而在第三种情况下,您只会看到一个层({...}
),因为对象正在自动展开展开。
第一种情况和后两种情况的区别在于,如果将 optionalSquare
设置为 nil
,后两种情况会给您一个运行时错误。使用第一种情况下的语法,您可以执行以下操作:
if let sideLength = optionalSquare?.sideLength {
println("sideLength is not nil")
} else {
println("sidelength is nil")
}
现有的正确答案很好,但我发现要完全理解这一点,我需要一个很好的类比,因为这是一个非常抽象和奇怪的概念。
所以,让我帮助那些“右脑”(视觉思维)开发人员,除了正确答案之外,还给出不同的观点。这是一个很好的类比,对我帮助很大。
生日礼物包装类比
将可选物品想象成生日礼物,包装在坚硬、坚硬、彩色的包装中。
在打开礼物之前,您不知道包装内是否有任何东西——也许里面什么都没有!如果里面有东西,那可能是另一个礼物,它也被包裹着,也可能什么都没有。您甚至可能打开 100 个嵌套的礼物,最终发现除了包装之外什么都没有。
如果可选项的值不是 nil
,那么现在您已经显示了一个包含 something 的框。但是,特别是如果该值没有显式键入并且是一个变量而不是预定义的常量,那么您可能仍然需要打开盒子,然后才能知道盒子中的任何具体内容,例如 < em>什么类型,或者实际的值是什么。
盒子里装了什么?!比喻
即使您打开了变量,您仍然像 last scene in SE7EN 中的布拉德·皮特(警告:剧透和非常 R 级的粗话和暴力),因为即使您打开了现在,您处于以下情况:您现在有 nil
,或者一个包含 某物的盒子(但您不知道是什么)。
您可能知道 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)
Swift 非常重视类型安全。整个 Swift 语言的设计都考虑到了安全性。这是 Swift 的标志之一,你应该张开双臂欢迎它。它将有助于开发干净、可读的代码,并帮助防止您的应用程序崩溃。
Swift 中的所有选项都用 ?
符号划分。通过在您声明为可选的类型的名称之后设置 ?
,您实际上 强制转换 this 不是 ?
之前的类型,而是作为 可选类型。
注意:变量或 Int 类型与 Int? 不同。它们是不能相互操作的两种不同类型。
使用可选
var myString: String?
myString = "foobar"
这不意味着您正在使用 String
类型。这意味着您正在使用 String?
类型(可选字符串或可选字符串)。事实上,每当你试图
print(myString)
在运行时,调试控制台将打印 Optional("foobar")
。 “Optional()
”部分表示此变量在运行时可能有值,也可能没有值,但恰好当前包含字符串“foobar”。除非您执行所谓的“展开”可选值,否则此“Optional()
”指示将保留。
展开一个可选的意味着你现在将该类型转换为非可选的。这将生成一个新类型并将驻留在该可选类型中的值分配给新的非可选类型。通过这种方式,您可以对该变量执行操作,因为编译器已保证它具有可靠的值。
Conditionally Unwrapping 将检查可选项中的值是否为 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 不同,每当尝试强制解包一个可选项,并且该可选项尚未设置且为 nil
时,当您尝试解开可选项以查看应用程序内部的内容时,将会崩溃。
使用类型转换解包。正如我们之前所说,虽然您是 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!
强制转换类型时,您会强制将该变量向下转换,就好像您是 certain 它属于该类型,因此可以安全操作而不是使您的应用程序崩溃。只要变量确实是您将其转换为的类型,就可以了,否则您的手会一团糟。
尽管如此,使用 as!
进行强制转换将允许您的代码编译。使用 as?
进行投射是另一回事。事实上,as?
将您的 Int
声明为完全不同的数据类型。
现在是 Optional(0)
如果你曾经尝试过写作业,比如
1 + Optional(1) = 2
你的数学老师可能会给你一个“F”。与斯威夫特相同。除了 Swift 宁愿不编译也不愿给你评分。因为在一天结束时,可选实际上可能为零。
安全第一的孩子。
https://i.stack.imgur.com/QPjq3.png
在 Swift 中有 7 种方法可以解开可选值。
可选绑定 - 安全可选链 - 安全可选模式 - 安全 Nil 合并 - 安全防护语句 - 安全强制展开 - 不安全隐式展开 - 不安全
init()
函数中初始化所有非可选方法。我建议阅读 Apple 发布的 Swift 书籍中的“基础”(涵盖可选)、“初始化”和“可选链”章节。