用于Swift可选类型的'Rosetta Stone'?

时间:2014-10-02 16:44:20

标签: syntax swift semantics optional

我掌握(我认为)optional types in Swift的基础知识,粗略地理解?!之间的区别,但我仍然对我得到的一些结果感到困惑使用这些功能 - 特别是Some <T>的作用,以及它与<T>本身的区别;我在某些情况下得到的一些特定错误消息;以及在我期望Some <T>的情况下,<T>似乎如何弹出。

但我也觉得,即使我理解个别情况,我对图片的掌握也远离我,我觉得这里有一个代码,如果我完全理解一个简单示例 - 如果您愿意,可以使用Rosetta Stone - !?,可选值和解包。

例如,这里是一个简单的(我认为)基本案例的详尽目录:

class Foo {
    var one:String = "";
    var two:String?
    var three:String!
}

let test = Foo()        // {one "" nil nil}

test.one
//test.one?             //  ERROR: ? requires optional type
//test.one!             //  ERROR: ! requires optional type

// ?, unassigned
test.two                // nil
test.two?               // nil
//test.two!             // ERROR: EXEC_BAD_INSTRUCTION

test.two == nil         // true
test.two? == nil        // true
//test.two! == nil      // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

//test.two.isEmpty      // ERROR: String? does not have .isEmpty
test.two?.isEmpty       // nil
//test.two!.isEmpty     // ERROR: EXEC_BAD_INSTRUCTION

// !, unassigned
test.three              // nil
test.three?             // nil
//test.three!           // ERROR: EXEC_BAD_INSTRUCTION

test.three == nil       // true
test.three? == nil      // true
//test.three! == nil    // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

//test.three.isEmpty    // ERROR: EXEC_BAD_INSTRUCTION
test.three?.isEmpty     // nil
//test.three!.isEmpty   // ERROR: EXEC_BAD_INSTRUCTION


test.two = "???"        // {one "" {Some "???"} nil}
test.three = "!!!"      // {one "" {Some "???"} three "!!!"}

// ?, assigned
test.two                // {Some "???"}
test.two?               // {Some "???"}
test.two!               // "???"

test.two == nil         // false
test.two? == nil        // false
//test.two! == nil      // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

//test.two.isEmpty      // ERROR: String? does not have .isEmpty
test.two?.isEmpty       // {Some false}
test.two!.isEmpty       // false

// !, assigned
test.three              // "!!!"
test.three?             // {Some "!!!"}
test.three!             // "!!!"

test.three == nil       // false
test.three? == nil      // false
//test.three! == nil    // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

test.three.isEmpty      // false
test.three?.isEmpty     // {Some false}
test.three!.isEmpty     // false

如果有人可以对此进行注释,并解释每个案例的内容,我认为这个答案可以作为Swift的这些功能如何运作的可靠参考。

1 个答案:

答案 0 :(得分:4)

好吧,我将尝试回答这一切。可能没有领带来完成所有事情:

注意:如有错误,请随时与我联系。这需要一段时间,所以我肯定做了一些。

快速说明:Optional实际上是一个枚举。它有两种状态:.None.Some(T),其中T是值的类型(在您的情况下为String)。

test.one

Foo有一个名为one的属性,返回String。一个明确的String,而不是一个可选的,意味着它肯定有一个值。您对此的处理方式与在代码中编写"HI!"的方式类似。这个值实际上是""

//test.one?             //  ERROR: ? requires optional type

这是一个错误,因为如上所述,test.one会返回一个明确的String,因此它不可能是零。您可以保证返回值存在。

//test.one!             //  ERROR: ! requires optional type

与?相同。的!是一个强制解包运算符,意味着有一个机会 test.one可能是nil,但是你想要强制取出值(如果不是则崩溃)那里)。但是,没有机会它是零,所以你不能拥有?还是一个!。

test.two                // nil

test.twoString?可以为零。因为它是可选的,所以允许您像在代码中一样返回nil。这个的真正价值是.None,所以你看到的值实际上是一个字符串?不是字符串。

test.two?               // nil

这基本上与上面的相同,除非您明确说明该值可能为零。

//test.two!             // ERROR: EXEC_BAD_INSTRUCTION

您永远不能在!值上使用nil而不会让它崩溃。当你使用这个操作符时,它会强制一个值(所以你有一个String,而不是String?)。但是,如果值为nil,则强制输出 no 值,因此最终导致程序崩溃。

test.two == nil         // true

test.two显然返回nil,或.None(它们是等效的)。因此,如果您比较nil == nil.None == .None,则确实如此。

test.two? == nil        // true

与上面相同。

//test.two! == nil      // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

强制解包nil值每次都会使程序崩溃。它也没有意义,因为强制解包会返回String,而不是String?String不能是nil

//test.two.isEmpty      // ERROR: String? does not have .isEmpty

基本上,无论何时想要在可选项上调用方法,都需要使用可选绑定或可选链接(两个单独的东西)确保它具有值。串?等于Optional.Some(String),你需要通过可选层来获得你想要的字符串。

test.two?.isEmpty       // nil

这里使用可选链接。基本上,这种方式的工作方式是test.two被评估。如果值为.Some(String),则在字符串上调用isEmpty。否则,如果是.None,则没有任何反应。这些可选链可以在每个语句中出现多行,例如test.two?.firstEmoji?(假设已经实现了这样的方法。

//test.two!.isEmpty     // ERROR: EXEC_BAD_INSTRUCTION

同样,强制展开零选项是不好的。如果没有先检查该值是否为.Some,请不要这样做。

test.three              // nil

由于three被隐式展开,并且它已初始化为nil(未被设置为其他内容),因此这不应该令人惊讶。

test.three?             // nil

这不是你可能在实际代码中使用的东西,因为它本质上是可选的链接,但在它之后没有任何东西。但是在这里,由于.three被隐式展开?具有&#34;重新包装&#34;它:结果的类型现在是String?。这在这里没什么区别,但是在test.three分配了String值之后,请查看下面的内容。

//test.three!           // ERROR: EXEC_BAD_INSTRUCTION

如上所述无法解开nil。这可能看起来令人困惑,因为声明通常被描述为产生一个隐含地展开的变量&#34 ;;但是如果它不是 nil&#34;那么它应该被理解为&#34;隐式解包

test.three == nil       // true
test.three? == nil      // true
//test.three! == nil    // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

与上述2相同。如果你有一个强制解包的变量,一个?似乎没有强行打开它,这是我不建议的行为。尝试不经常使用强制解包的选项,如果你真的需要,主要是与部分UI有关。很多时候,当你不期待它时它会崩溃。

//test.three.isEmpty    // ERROR: EXEC_BAD_INSTRUCTION
test.three?.isEmpty     // nil
//test.three!.isEmpty   // ERROR: EXEC_BAD_INSTRUCTION

如果未指定可选项,则默认为nil。如果你然后试着强行打开它...我想你明白了。第一行和第三行尝试从nil上的String调用方法(在ObjC中工作,而不是Swift)。第二个使用可选链接在调用方法之前检查它是否为零,因为它知道它是零,所以它不能。

test.two = "???"        // {one "" {Some "???"} nil}
test.three = "!!!"      // {one "" {Some "???"} three "!!!"}

这会将test.two设为.Some("???")test.three等于.Some("!!!")您看到的输出只会显示该类中保存的所有变量,以及它们的变化方式。< / p>

test.two                // {Some "???"}
test.two?               // {Some "???"}
test.two!               // "???"

test.two现在是.Some("???"),所以当你调用它时,返回的是:一个字符串?有价值的。强制解包时,它现在返回.Some中保存的值而不会崩溃,因为其中确实存在一个字符串。

test.two == nil         // false
test.two? == nil        // false

test.two仍然是可选的,所以在前两个中,当它将它们与nil进行比较时,它意识到,&#34;嘿,有.Some值,所以不是 nil。&#34;

//test.two! == nil      // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

强制解包值会从字符串中转换test.two的值?到一个字符串。字符串永远不会是零,因为如果它们是,它们将需要是可选的。将一个绝对是String的值与nil进行比较是没有意义的,因为你知道一个事实它不是nil;否则,程序会崩溃之前!

//test.two.isEmpty      // ERROR: String? does not have .isEmpty

test.two是String ?,而不是String。为了访问字符串本身,您需要确保它可以访问,使用?还是一个!

test.two?.isEmpty       // {Some false}

这说,&#34;如果test.two包含一个字符串(不是nil),则查找它是否为空。&#34;它说{Some false}因为你正在访问一个Optional的成员,而不是一个直接的String。

test.two!.isEmpty       // false

!,另一方面, 返回一个字符串。在字符串上调用.isEmpty可以是truefalse,在这种情况下是false,因为您将其设置为非空字符串。

test.three              // "!!!"

test.three强制 - 从中​​解包String,在这种情况下,因为它有一个值,所以它可以工作。

test.three?             // {Some "!!!"}

您将此视为普通可选(不强制解包),因此您获得的是Some(String)而不仅仅是String。

test.three!             // "!!!"

由于你在声明中强行打开它,所以它在这里被强行打开。

test.three == nil       // false

这是另一种奇怪的行为,因为它应该是一个错误。它应该是一个String,无法与nil进行比较,但这里有一些古怪的东西。当我发现这件事时,我会回复你。

test.three? == nil      // false

test.three视为普通可选项,并检查其状态是.None还是无。

//test.three! == nil    // ERROR: Cannot invoke == with an argument list of type (@lvalue String, NilLiteralConvertable)

上面的那两个应该是什么样的。无法将String与nil进行比较,因此会抛出错误。

test.three.isEmpty      // false

查看被强制输出的字符串值(存在;否则,它会崩溃)。该字符串不为空,因此它是错误的。

test.three?.isEmpty     // {Some false}

将其视为字符串?如果test.three不是nil,则它从.Some(一个String)获取值,并计算它是否为空,而不是。

test.three!.isEmpty     // false

String被强制退出可选项,isEmpty直接调用它。它不是空的,因此返回false。

我希望我能帮助澄清事情,我会告诉你为什么一个案例就是我自己找到的方式:]