3

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

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

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

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

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

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

然而,关于后者,斯威夫特的“纯粹主义者”将此称为“代码气味”,并坚持认为这是“糟糕、糟糕、糟糕!!”......但他们从不解释原因!所以……为什么会这么糟糕?

注意:是的,我知道 Optional 链接和其他此类功能,它们不能与隐式展开的选项一起使用,它们都是非常酷的功能,但我特别询问针对 nil 测试隐式展开的选项的技术缺点vs 可选绑定。

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

更新

实际上,我只是找到了一种方法来解决我的一个问题(至少以肤浅的方式),即必须创建一个新变量来保存未包装的变量。这里的诀窍是,由于展开的变量实际上与可选变量本身位于不同的范围内,因此您可以使用完全相同的名称。

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

  1. 可选的
  2. 展开的可选
  3. 括号内的一个新的局部变量

这是显示此内容的疯狂代码...

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 
}

再说一次,我不会宽恕这个!!我只是展示了一些其他人可能会从具有指导意义的角度觉得有趣的代码。我确实做到了!

4

3 回答 3

8

确实有一个简单的原因,它就是您列出的原因之一:“更好的安全检查”。可选绑定使编译器知道展开的范围,而程序员有责任跟踪您何时检查或没有检查隐式展开的可选 for 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,所以它们必须是可选的。通常@IBOutlets 属于这一类:您可以相信它们有一个值,并且您不想一直打开它们,但是它们没有被分配 at 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.
}
于 2016-07-13T01:33:40.483 回答
5

由于Swift 3中隐式解包选项的更改,如果您正确解包常规选项而不是使用隐式解包选项,您会更开心。

Swift 3中,如果您将 a 分配String!给一个新变量,则该变量的类型为String?。这意味着您的隐式解包选项在赋值时成为常规选项,您现在必须处理解包新变量。

此代码适用于Swift 2.x

let stringA: String! = "Hello"

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

斯威夫特 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 '?'?
}

如果改为使用 aString?并将其解包,则代码在Swift 2.xSwift 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 的简单方法,或者缺乏 可以更优雅地处理某些代码模式的语言功能。因此,我们 希望限制它们的使用,并引入更 具体的语言功能来取代它们。除了一些 特定的场景,可选总是更安全的选择,我们希望 鼓励人们使用它们而不是 IUO。

该提案旨在将 IUO 的采用限制在 实际需要它们的地方,并让 Swift 语言走上在 其他技术使它们变得不必要时完全从系统中删除隐式解包选项的道路上。 它还完全废除了编译器类型检查器级别以下的任何 IUO 概念,这将大大简化编译器的实现。

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

于 2016-07-13T02:26:06.683 回答
3

让我们弄清楚IUO的真正目的。它们最初纯粹是作为与 Objective-C 通信的设备。理论上,任何来自 Objective-C 的对象都可能nil,所以一开始每个来自 Objective-C 的对象都是可选的。让每一个这样的 Optional 都是真正的 Optional 太破坏性了,所以这样的每个对象都是隐式展开的 Optional。

然而,随后,Apple 开始手动调整 API 以告诉 Swift 来自 Objective-C 的对象是否实际上可以是nil. 手动调整过程现在即将完成(Swift 3)。因此,来自 Objective-C 的所有东西现在要么是普通对象,要么是普通的 Optional(或者,在某些情况下,你必须通过 atry来获取的普通对象)。

既然如此,实际上根本不需要隐式展开的 Optionals。在这一点上,它们对程序员来说只不过是一种懒惰的便利。其他语言设备的出现使它们变得不必要。例如,打开一个 Optionalif let并被迫添加一个级别的花括号是很痛苦的,但现在我们有了guard let这样您就可以在不添加一个级别的花括号的情况下展开。

苹果因此开始拧紧螺丝,让 IUO 越来越不方便。例如,正如 vacawama 所说的那样,它们不再通过分配传播。并且不再有 IUO 数组之类的东西(例如,[Int!]不再是一种类型)。

这个过程将一直持续到几乎没有 IUO 合法的情况。因此最好现在就改掉使用它们的习惯。

于 2016-07-13T03:12:23.667 回答