使用隐式解包的选项但是测试nil vs可选绑定是否有技术上的缺点?

时间:2016-07-13 01:21:22

标签: swift optional optional-binding

好的,所以我知道在Swift中使用选项的正常方法是通过可选的绑定来打开它们,就像这样......

let stringA:String? = nil // (or "ABC")

if let unwrappedStringA = stringA
{
    let x:String = unwrappedStringA
}

但是我也看到了使用隐式解包的选项的以下方式,我个人认为它看起来更干净,因为它不仅不需要额外的变量,而且还“更好地阅读”,更像英语(即'如果这不是零,那么......')和可读性是Swift的核心原则之一(特别是在即将推出的Swift 3.0中)。

let stringA:String! = nil // (or "ABC")

if stringA != nil
{
    let x:String = stringA
}

然而,对于后者,斯威夫特的“纯粹主义者”将此称为“代码味”,并坚持认为它是“坏,坏,坏!!”......但他们从未解释过为什么!那么......为什么这么糟糕?

注意:是的,我知道可选链接和其他这些功能,你不能使用隐式解包的选项,它们都是非常酷的功能,但我特别询问测试的技术缺点针对nil与可选绑定的隐式解包选项。

我所希望的是可量化的,技术上的原因,为什么一个比另一个好(即编译器优化,更好的安全检查,性能等)换句话说,不仅仅是'嘿,因为那是不是'Swifty'的方式来做到这一点!'希望这是有道理的。

更新

我实际上刚刚找到了一种方法来解决我的一个问题(至少以一种表面的方式),它必须创建一个新变量来保存未包装的变量。这里的技巧是因为展开的变量实际上与可选的本身不同,你可以使用完全相同的名称。

此外,括号内部还有另一个范围,它也可以有一个名为stringA的变量。这意味着你现在可以拥有三个'stringA'变量(但并不意味着你应该!)......

  1. 可选
  2. 未包装的可选
  3. 括号内的新的局部变量
  4. 这是一个显示这个疯狂的代码......

    let stringA:String? = nil // (or "ABC") // First stringA variable
    
    if let stringA = stringA
    {
        let x:String = stringA // <- This stringA is the unwrapped optional (the second stringA)
    
        // Now we're just getting crazy (and smelly!)
        let stringA = stringA + stringA // <- This is yet another stringA (third one!)
        let y:String = stringA 
    }
    

    再一次,我不是在纵容这个!!我只是从一个有启发性的角度展示其他人可能会感兴趣的代码。我确实做到了!

3 个答案:

答案 0 :(得分:8)

确实有一个简单的原因,它是您列出的一个原因:&#34;更好的安全检查&#34;。可选绑定使编译器保持解包的范围,而程序员有责任跟踪您何时检查或未检查nil的隐式解包的可选项。

使用可选绑定:

let stringA:String? = nil // (or "ABC")

if let unwrappedStringA = stringA
{
    let x:String = unwrappedStringA
}

accidentallyAttemptingToUse(stringA) // Compiler error! I see my mistake.

隐式展开:

let stringA:String! = nil // (or "ABC")

if(stringA != nil)
{
    let x:String = stringA
}

accidentallyAttemptingToUse(stringA) // Runtime crash I might find in testing. Yuck.

是什么 IUO适用于哪些?

为什么要隐式展开选项呢?它们的存在基本上只有一个目的:肯定有一个值的属性,但直到init之后,它们必须是可选的。通常@IBOutlet属于此类别:您可以相信他们有价值,并且您不想一直打开它们,但它们不会在init分配,所以你必须让它们成为可选的。 是隐式解包的选项。

展开同名变量

您的更新暂时建议将选项包解包为具有相同名称的变量,实际上是优秀的 Swift风格,并且早期和经常被Apple使用。当您打开同名变量时,代码更容易阅读,因为您可以跟踪更少(和更好)的名称。绝对拥抱这个!

let stringA:String? = nil // (or "ABC")

if let stringA = stringA
{
    let x:String = stringA // Of *course* this is still named `stringA`;
    // it's representing the same concept, after all.
}

答案 1 :(得分:5)

由于change to implicitly unwrapped optionals in Swift 3,如果您正确展开常规选项而不是使用隐式展开的选项,您会更高兴。

Swift 3 中,如果您为新变量指定String!,则该变量的类型为String?。这意味着你隐式展开的可选项在赋值时成为常规可选项,现在你必须处理解包新变量。

此代码适用于 Swift 2.x

let stringA: String! = "Hello"

if stringA != nil
{
    let y = stringA
    let z = y + " world!"
}

Swift 3

let stringA: String! = "Hello"

if stringA != nil
{
    let y = stringA
    let z = y + " world!"  // ERROR: value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?
}

如果你使用String?并打开它,那么代码在 Swift 2.x Swift 3 中按预期工作:

let stringA: String? = "Hello"

if let stringA = stringA
{
    let y = stringA
    let z = y + " world!"
}

请参阅此处有关此更改的官方讨论:SE-0054: Abolish ImplicitlyUnwrappedOptional Type

本文件的动机部分指出[强调添加我的]:

  

ImplicitlyUnwrappedOptional(“IUO”)类型是一个有价值的工具   导入Objective-C API,其中参数的可空性或   返回类型未指定。它也代表了一种方便的机制   用于解决初始化程序中的明确初始化问题。   然而,IUO是一种过渡技术;它们代表一种简单的   解决未注释API的方法,或缺乏语言功能   可以更优雅地处理某些代码模式。因此,我们   希望限制其使用前进,并引入更多   取代他们的特定语言功能。除了少数   特定场景,期权总是更安全的赌注,我们希望   鼓励人们使用它们代替IOO。

     

此提案旨在将IUO的采用限制在的地方   实际上是必需的,并将Swift语言放在的路径上   时完全从系统中删除隐式解包的选项   其他技术使它们变得不必要。它也完全没有   废除IOO的任何概念低于类型检查器级别   编译器,这将大大简化编译器   实施

这清楚地表明语言设计者认为您应该尽可能少地使用隐式解包的选项

答案 2 :(得分:3)

让我们清楚IUO的真正目的。它们纯粹是作为与Objective-C进行通信的设备而发明的。从理论上讲,来自Objective-C 的任何对象可能<{1>},所以在开头来自Objective-C的每个对象都是可选的。让每个这样的Optional都成为一个真正的Optional是太具有破坏性了,所以这样的每个对象都是一个隐式解包的Optional。

然而,Apple开始手动调整API以告诉Swift来自Objective-C 的对象实际上是否nil。手动调整过程即将完成(Swift 3)。因此,来自Objective-C的所有内容现在都是普通对象或普通的可选对象(或者,在某些情况下,您需要通过nil来获取正常对象)。

就是这样,实际上根本不需要隐式展开的Optionals。在这一点上,它们只是程序员的懒惰。还有其他语言设备使它们变得不必要。例如,用try打开Optional是一件很痛苦的事情,并且被迫添加一个级别的大括号​​,但现在我们有if let所以你可以打开而不用添加一个花括号的水平。

因此,Apple开始拧紧螺丝,使IUO变得越来越不方便。例如,正如vacawama正确地说的那样,他们不再通过任务传播。并且不再存在IUO数组(例如,guard let不再是类型)。

此过程将持续到IUO合法的上下文(如果有)为止。因此,最好不要习惯使用 now