首页 文章

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

提问于
浏览
150

我正在按照this tutorial学习Swift for iOS 8 / OSX 10.10,并且多次使用术语“ unwrapped value ”,如本段(在Objects和Class下):

使用可选值时,您可以写吗?在诸如方法,属性和下标之类的操作之前 . 如果之前的值?是零,一切都在?被忽略,整个表达式的值为零 . 否则,可选值是解包,并且后面的一切都是?对未包装的 Value 采取行动 . 在这两种情况下,整个表达式的值都是可选值 .

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

我没有得到它,并在网上搜索没有运气 .

这意味着什么?


编辑

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

原始代码

Original code

Cezary的解决方案

Cezary's solution

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

在这两种情况下,结果不应该相同吗?

Related Q&A : What is an optional value in Swift?

4 回答

  • 13

    首先,您必须了解可选类型是什么 . 可选类型基本上意味着变量可以是 nil .

    例:

    var canBeNil : Int? = 4
    canBeNil = nil
    

    问号表示 canBeNil 可以 nil .

    这不起作用:

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

    要从变量中获取值,如果它是可选的,则必须 unwrap it . 这只是意味着在最后加上感叹号 .

    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
    

    编辑:

    您所看到的差异正是可选值为 wrapped 这一事实的症状 . 它上面还有另一层 . unwrapped 版本只显示直接对象,因为它是完全展开的 .

    快速游乐场比较:

    playground

    在第一种情况和第二种情况下,对象不会自动解包,因此您会看到两个"layers"( {{...}} ),而在第三种情况下,您只看到一个图层( {...} ),因为该对象正在自动解包 .

    第一种情况和后两种情况之间的区别在于,如果 optionalSquare 设置为 nil ,则后两种情况将给出运行时错误 . 使用第一种情况中的语法,您可以执行以下操作:

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

    现有的正确答案很棒,但我发现,要让我完全理解这一点,我需要一个很好的类比,因为这是一个非常抽象和奇怪的概念 .

    所以,除了正确答案之外,让我通过提供不同的视角来帮助那些“右脑”(视觉思维)开发人员 . 这是一个很好的比喻,对我帮助很大 .

    Birthday Present Wrapping Analogy

    将可选项视为像生日礼物一样,采用坚硬,坚硬,彩色的包装 .

    在你打开礼物之前,你不要在包装内放任何东西 - 也许内部什么也没有!如果里面有东西,它可能是另一个存在,它也被包裹,也可能不包含任何内容 . 你甚至可能打开100个嵌套的礼物,最后发现除了包装之外什么都没有 .

    如果Optional的值不是 nil ,那么现在您已经显示了一个包含内容的框 . 但是,特别是如果值没有明确地键入并且是变量而不是预定义的常量,那么您可能仍然需要先打开框,然后才能知道关于框中的内容的任何具体内容,例如它是什么类型,或者是什么实际 Value 是 .

    What's In The Box?! Analogy

    即使你打开变量,你仍然像last scene in SE7EN中的布拉德皮特(警告:破坏者和非常R级的粗言秽语和暴力),因为即使在你打开礼物之后,你仍处于以下情况: you now have nil, or a box containing something (but you don't know what).

    你可能知道某事的类型 . 例如,如果您将变量声明为类型 [Int:Any?] ,那么您就知道您有一个带有整数下标的(可能为空)Dictionary,它会生成任何旧类型的包装内容 .

    这就是为什么在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)
    
  • 0

    '?'表示可选的链接表达式

    '!'的意思是强制值

    enter image description here

  • 15

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

    Swift中的所有选项都使用 ? 符号划分 . 通过在您声明为可选类型的名称之后设置 ? ,您基本上将其转换为 ? 之前的类型,而是作为可选类型 .


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


    Using Optional

    var myString: String?
    
    myString = "foobar"
    

    这确实 not 表示您正在使用 String 类型 . 这意味着您正在使用 String? (String Optional或Optional String)类型 . 事实上,每当你尝试

    print(myString)
    

    在运行时,调试控制台将打印 Optional("foobar") . “ 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”可选值 .

    Unwrapping 可选表示您现在将该类型转换为非可选类型 . 这将生成一个新类型,并将该可选内容中的值分配给新的非可选类型 . 这样您就可以对该变量执行操作,因为编译器已经保证它具有可靠的值 .


    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")
    }
    

    Forcibly Unwrapping 是通过使用所谓的 ! ("bang")运算符完成的 . 这不太安全,但仍然允许您的代码编译 . 但是,无论何时使用bang运算符,在强行展开之前,必须确保变量确实包含一个实数值 .

    var myString: String?
    
    myString = "foobar"
    
    print(myString!)
    

    以上是完全有效的Swift代码 . 它打印出 myString 的值,该值设置为"foobar" . 用户将在控制台中看到 foobar 并且's about it. But let'假设从未设置过该值:

    var myString: String?
    
    print(myString!)
    

    现在我们手上有不同的情况 . 与Objective-C不同,只要尝试强制解包可选项,并且未设置可选项且 nil ,当您尝试打开可选项以查看应用程序中的内容将崩溃时 .


    Unwrapping w/ Type Casting . 正如我们之前所说的那样,当你是 unwrapping 可选实际上你正在转换为非可选类型时,你也可以将非可选类型转换为其他类型 . 例如:

    var something: Any?
    

    在我们的代码中的某个地方,变量 something 将设置一些值 . 也许我们正在使用泛型,或者可能还有一些其他逻辑会导致这种情况发生变化 . 所以稍后在我们的代码中我们想要使用 something 但是如果它是不同的类型仍然能够区别对待它 . 在这种情况下,您将需要使用 as 关键字来确定:

    注意: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
    

    注意两个 as 关键字之间的区别 . 就像之前我们强行打开一个可选项一样,我们使用 ! bang运算符来执行此操作 . 在这里你将做同样的事情,但不是像只是一个非可选的铸造,你也将它作为 Int . 并且它必须能够被转发为 Int ,否则,就像使用bang运算符一样,当值为 nil 时,您的应用程序将崩溃 .

    并且为了在某种类型或数学运算中使用这些变量,必须将它们解包以便这样做 .

    例如,在Swift中,只有相同种类的有效数字数据类型可以相互操作 . 当您使用 as! 转换类型时,您正在强制转换该变量,就好像它是 certain 那样,因此可以安全地操作并且不会使应用程序崩溃 . 只要变量确实属于你所使用的类型,这是可以的,否则你手上就会弄得一团糟 .

    尽管如此,使用 as! 进行转换将允许您的代码进行编译 . 通过铸造 as? 是一个不同的故事 . 实际上, as?Int 声明为完全不同的数据类型 .

    Now it is Optional(0)

    如果你曾经尝试写过类似的东西

    1 + Optional(1) = 2
    

    你的数学老师可能会给你一个"F" . 与Swift相同 . 除了Swift之外,根本不会编译,而不是给你一个分数 . Because at the end of the day the optional may in fact be nil .

    安全第一孩子 .

相关问题