ios Swift 中的“解包值”是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24034483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is an "unwrapped value" in Swift?
提问by Bigood
I'm learning Swift for iOS 8 / OSX 10.10 by following this tutorial, and the term "unwrapped value" is used several times, as in this paragraph (under Objects and Class):
我正在按照本教程学习适用于 iOS 8 / OSX 10.10 的 Swift ,并且多次使用了术语“解包值”,如本段中所示(在Objects 和 Class 下):
When working with optional values, you can write ? before operations like methods, properties, and subscripting. If the value before the ? is nil, everything after the ? is ignored and the value of the whole expression is nil. Otherwise, the optional value is unwrapped, and everything after the ? acts on the unwrapped value. In both cases, the value of the whole expression is an optional value.
使用可选值时,您可以编写 ? 在方法、属性和下标等操作之前。如果 ? 之前的值 是零,之后的一切?被忽略,整个表达式的值为 nil。否则,可选值将被解包,而 ? 作用于展开的值。在这两种情况下,整个表达式的值都是一个可选值。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
I don't get it, and searched on the web without luck.
我不明白,并没有运气在网上搜索。
What does this means?
这是什么意思?
Edit
编辑
From Cezary's answer, there's a slight difference between the output of the original code and the final solution (tested on playground) :
从 Cezary 的回答来看,原始代码的输出与最终解决方案(在操场上测试)之间略有不同:
Original code
原码
Cezary's solution
Cezary的解决方案
The superclass' properties are shown in the output in the second case, while there's an empty object in the first case.
在第二种情况下,超类的属性显示在输出中,而在第一种情况下有一个空对象。
Isn't the result supposed to be identical in both case?
在这两种情况下,结果不是应该相同吗?
Related Q&A : What is an optional value in Swift?
相关问答:Swift 中的可选值是什么?
回答by Cezary Wojcik
First, you have to understand what an Optional type is. An optional type basically means that the variable can benil
.
首先,您必须了解什么是 Optional 类型。可选类型基本上意味着变量可以是nil
.
Example:
例子:
var canBeNil : Int? = 4
canBeNil = nil
The question mark indicates the fact that canBeNil
can be nil
.
问号表明一个事实,即canBeNil
可以nil
。
This would not work:
这行不通:
var cantBeNil : Int = 4
cantBeNil = nil // can't do this
To get the value from your variable if it is optional, you have to unwrap it. This just means putting an exclamation point at the end.
要从变量中获取值(如果它是可选的),您必须解开它。这只是意味着在末尾加上一个感叹号。
var canBeNil : Int? = 4
println(canBeNil!)
Your code should look like this:
您的代码应如下所示:
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare!.sideLength
A sidenote:
旁注:
You can also declare optionals to automatically unwrap by using an exclamation mark instead of a question mark.
您还可以使用感叹号而不是问号来声明可选项以自动展开。
Example:
例子:
var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed
So an alternative way to fix your code is:
因此,修复代码的另一种方法是:
let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare.sideLength
EDIT:
编辑:
The difference that you're seeing is exactly the symptom of the fact that the optional value is wrapped. There is another layer on top of it. The unwrappedversion just shows the straight object because it is, well, unwrapped.
您所看到的差异正是可选值被包装这一事实的症状。它上面还有一层。在展开的版本只是表明直对象,因为它是很好,解开。
A quick playground comparison:
一个快速的游乐场比较:
In the first and second cases, the object is not being automatically unwrapped, so you see two "layers" ({{...}}
), whereas in the third case, you see only one layer ({...}
) because the object is being automatically unwrapped.
在第一种和第二种情况下,对象不会自动展开,因此您会看到两个“层” ( {{...}}
),而在第三种情况下,您只能看到一个层 ( {...}
),因为对象正在自动展开。
The difference between the first case and the second two cases is that the second two cases will give you a runtime error if optionalSquare
is set to nil
. Using the syntax in the first case, you can do something like this:
第一种情况和后两种情况的区别在于,后两种情况如果optionalSquare
设置为 ,则会出现运行时错误nil
。使用第一种情况下的语法,您可以执行以下操作:
if let sideLength = optionalSquare?.sideLength {
println("sideLength is not nil")
} else {
println("sidelength is nil")
}
回答by CommaToast
The existing correct answer is great, but I found that for me to understand this fully, I needed a good analogy, since this is a very abstract and weird concept.
现有的正确答案很好,但我发现要完全理解这一点,我需要一个很好的类比,因为这是一个非常抽象和奇怪的概念。
So, let me help those fellow "right-brained" (visual thinking) developers out by giving a different perspective in addition to the correct answer. Here is a good analogy that helped me a lot.
因此,让我通过给出正确答案之外的不同视角来帮助那些“右脑”(视觉思维)开发人员。这是一个很好的类比,对我帮助很大。
Birthday Present Wrapping Analogy
生日礼物包装类比
Think of optionals as being like birthday presents that come in stiff, hard, colored wrapping.
把可选的东西想象成像生日礼物一样,用坚硬、坚硬的彩色包装。
You don't know if there's anything inside the wrapping until you unwrap the present — maybe there is nothing at all inside! If there is something inside, it could be yet another present, which is also wrapped, and which also might contain nothing. You might even unwrap 100 nested presents to finally discover there was nothing but wrapping.
在您打开礼物之前,您不知道包装内是否有任何东西——也许里面什么都没有!如果里面有东西,它可能是另一个礼物,它也被包裹着,也可能什么都不包含。您甚至可能打开 100 个嵌套的礼物,最终发现除了包装之外别无他物。
If the value of the optional is not nil
, now you have revealed a box containing something. But, especially if the value is not explicitly typed and is a variable and not a predefined constant, then you may still need to open the boxbefore you can know anything specific about what's in the box, like what typeit is, or what the actual valueis.
如果 optional 的值不是nil
,那么现在您已经显示了一个包含某些东西的盒子。但是,特别是如果该值没有明确键入并且是一个变量而不是预定义的常量,那么您可能仍然需要打开该框,然后才能了解有关框中内容的任何具体信息,例如它是什么类型,或者是什么实际价值是。
What's In The Box?!Analogy
盒子里有什么东西?!比喻
Even after you unwrap the variable, you are still like Brad Pitt in the last scene in SE7EN(warning: spoilers and very R-rated foul language and violence), because even after you have unwrapped the present, you are in the following situation: you now have nil
, or a box containing something(but you don't know what).
即使你解开变量,你仍然像SE7EN最后一幕中的布拉德皮特(警告:剧透和非常R级的粗言秽语和暴力),因为即使你解开礼物,你仍然处于以下情况:你现在有nil
,或者一个装有东西的盒子(但你不知道是什么)。
You might know the type of the something. For example, if you declared the variable as being type, [Int:Any?]
, then you'd know you had a (potentially empty) Dictionary with integer subscripts that yield wrapped contents of any old type.
您可能知道something的类型。例如,如果您将变量声明为 type, [Int:Any?]
,那么您就会知道您有一个(可能为空)带有整数下标的字典,可以生成任何旧类型的包装内容。
That is why dealing with collection types (Dictionaries and Arrays) in Swift can get kind of hairy.
这就是为什么在 Swift 中处理集合类型(字典和数组)会变得有点麻烦。
Case in point:
案例:
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)
回答by Brandon A
Swift places a high premium on type safety. The entire Swift language was designed with safety in mind. It is one of the hallmarks of Swift and one that you should welcome with open arms. It will assist in the development of clean, readable code and help keep your application from crashing.
Swift 非常重视类型安全。整个 Swift 语言的设计都考虑到了安全性。这是 Swift 的标志之一,您应该张开双臂欢迎它。它将有助于开发干净、可读的代码,并有助于防止您的应用程序崩溃。
All optionals in Swift are demarcated with the ?
symbol. By setting the ?
after the name of the type in which you are declaring as optional you are essentially castingthis not as the type in which is before the ?
, but instead as the optionaltype.
Swift 中的所有可选项都用?
符号标定。通过?
在您声明为可选的类型的名称之后设置 the ,您本质上不是将this转换为 之前的类型?
,而是作为可选类型。
Note: A variable or type
Int
is notthe same asInt?
. They are two different types that can not be operated on each other.
注意:变量或类型
Int
是不一样的Int?
。它们是不能相互操作的两种不同类型。
Using Optional
使用可选
var myString: String?
myString = "foobar"
This does notmean that you are working with a type of String
. This means that you are working with a type of String?
(String Optional, or Optional String). In fact, whenever you attempt to
但这并不意味着你是一个类型的工作String
。这意味着您正在使用一种类型String?
(String Optional 或 Optional String)。事实上,每当你试图
print(myString)
at runtime, the debug console will print Optional("foobar")
. The "Optional()
" part indicates that this variable may or may not have a value at runtime, but just so happens to currently be containing the string "foobar". This "Optional()
" indication will remain unless you do what is called "unwrapping" the optional value.
在运行时,调试控制台将打印Optional("foobar")
. " Optional()
" 部分表示这个变量在运行时可能有也可能没有值,但恰好当前包含字符串“foobar”。Optional()
除非您执行所谓的“解包”可选值,否则此“ ”指示将保留。
Unwrappingan optional means that you are now casting that type as non-optional. This will generate a new type and assign the value that resided within that optional to the new non-optional type. This way you can perform operations on that variable as it has been guaranteed by the compiler to have a solid value.
展开可选意味着您现在将该类型转换为非可选。这将生成一个新类型并将驻留在该可选中的值分配给新的非可选类型。这样您就可以对该变量执行操作,因为编译器已保证它具有可靠的值。
Conditionally Unwrappingwill check if the value in the optional is nil
or not. If it is not nil
, there will be a newly created constant variable that will be assigned the value and unwrappedinto the non-optional constant. And from there you may safely use the non-optional in the if
block.
有条件地展开将检查可选项中的值是否是nil
。如果不是nil
,则会有一个新创建的常量变量,该变量将被赋值并解包为非可选常量。从那里你可以安全地使用if
块中的非可选。
Note: You can give your conditionally unwrapped constant the same name as the optional variable you are unwrapping.
注意:您可以为有条件展开的常量指定与您正在展开的可选变量相同的名称。
if let myString = myString {
print(myString)
// will print "foobar"
}
Conditionally unwrapping optionals is the cleanest way to access an optional's value because if it contains a nil value then everything within the if let block will not execute. Of course, like any if statement, you may include an else block
有条件地解包可选项是访问可选项值的最干净的方法,因为如果它包含一个 nil 值,那么 if let 块中的所有内容都不会执行。当然,像任何 if 语句一样,您可以包含一个 else 块
if let myString = myString {
print(myString)
// will print "foobar"
}
else {
print("No value")
}
Forcibly Unwrappingis done by employing what is known as the !
("bang") operator. This is less safe but still allows your code to compile. However whenever you use the bang operator you must be 1000% certain that your variable does in fact contain a solid value before forcibly unwrapping.
强制展开是通过使用所谓的!
(“bang”)运算符来完成的。这不太安全,但仍然允许您的代码编译。但是,无论何时使用 bang 运算符,您都必须 1000% 确定您的变量在强制展开之前确实包含一个实值。
var myString: String?
myString = "foobar"
print(myString!)
This above is entirely valid Swift code. It prints out the value of myString
that was set as "foobar". The user would see foobar
printed in the console and that's about it. But let's assume the value was never set:
以上是完全有效的 Swift 代码。它打印出myString
设置为“foobar”的值。用户会foobar
在控制台中看到打印,仅此而已。但让我们假设从未设置过该值:
var myString: String?
print(myString!)
Now we have a different situation on our hands. Unlike Objective-C, whenever an attempt is made to forcibly unwrap an optional, and the optional has not been set and is nil
, when you try to unwrap the optional to see what's inside your application will crash.
现在我们手头的情况有所不同。与 Objective-C 不同的是,每当试图强行解开可选项时,并且可选项尚未设置并且是nil
,当您尝试解开可选项以查看应用程序中的内容时,就会崩溃。
Unwrapping w/ Type Casting. As we said earlier that while you are unwrapping
the optional you are actually casting to a non-optional type, you can also cast the non-optional to a different type. For example:
使用类型转换展开。正如我们之前所说,虽然您是unwrapping
可选的,但您实际上是将其转换为非可选类型,您也可以将非可选类型转换为不同的类型。例如:
var something: Any?
Somewhere in our code the variable something
will get set with some value. Maybe we are using generics or maybe there is some other logic that is going on that will cause this to change. So later in our code we want to use something
but still be able to treat it differently if it is a different type. In this case you will want to use the as
keyword to determine this:
在我们代码的某个地方,变量something
将被设置为某个值。也许我们正在使用泛型,或者可能有其他一些正在发生的逻辑会导致这种情况发生变化。所以稍后在我们的代码中,我们想要使用something
但如果它是不同的类型,仍然能够以不同的方式对待它。在这种情况下,您将需要使用as
关键字来确定这一点:
Note: The
as
operator is how you type cast in Swift.
注意:
as
操作符是你在 Swift 中输入类型转换的方式。
// 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
Notice the difference between the two as
keywords. Like before when we forcibly unwrapped an optional we used the !
bang operator to do so. Here you will do the same thing but instead of casting as just a non-optional you are casting it also as Int
. And it mustbe able to be downcast as Int
, otherwise, like using the bang operator when the value is nil
your application will crash.
注意这两个as
关键字之间的区别。就像之前我们强行解开一个可选项时一样,我们使用了!
bang 运算符来执行此操作。在这里,您将执行相同的操作,但不是将其转换为非可选类型,而是将其转换为Int
. 并且它必须能够向下转换为Int
,否则,就像当值为 时使用 bang 运算符一样,nil
您的应用程序将崩溃。
And in order to use these variables at all in some sort or mathematical operation they must be unwrapped in order to do so.
并且为了在某种类型或数学运算中完全使用这些变量,它们必须被解包才能这样做。
For instance, in Swift only valid number data types of the same kind may be operated on each other. When you cast a type with the as!
you are forcing the downcast of that variable as though you are certainit is of that type, therefore safe to operate on and not crash your application. This is ok as long as the variable indeed is of the type you are casting it to, otherwise you'll have a mess on your hands.
例如,在 Swift 中,只有相同类型的有效数字数据类型可以相互操作。当您使用as!
强制转换类型时,您将强制向下转换该变量,就好像您确定它属于该类型一样,因此可以安全地操作并且不会使您的应用程序崩溃。只要变量确实是您将其转换为的类型,就可以,否则您的手会一团糟。
Nonetheless casting with as!
will allow your code to compile. By casting with an as?
is a different story. In fact, as?
declares your Int
as a completely different data type all together.
尽管如此,使用 withas!
将允许您的代码编译。通过使用 an 进行铸造as?
是一个不同的故事。实际上,as?
将您的Int
所有数据类型声明为完全不同的数据类型。
Now it is Optional(0)
现在它是 Optional(0)
And if you ever tried to do your homework writing something like
如果你曾经尝试做作业写一些类似的东西
1 + Optional(1) = 2
Your math teacher would have likely given you an "F". Same with Swift. Except Swift would rather not compile at all rather than give you a grade. Because at the end of the day the optional may in fact be nil.
你的数学老师很可能会给你一个“F”。与斯威夫特相同。除了 Swift 宁愿根本不编译,也不愿给你打分。因为在一天结束时, optional 可能实际上是 nil。
Safety First Kids.
安全第一的孩子。