当前位置: 首页 > 知识库问答 >
问题:

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

夹谷浩宕
2023-03-14

好吧,我知道在Swift中使用optionals的正常方式是通过可选绑定来打开它们,就像这样。。。

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
}

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

注:是的,我知道可选链接和其他类似的功能,你不能使用隐式解包选项,它们都是非常酷的功能,但我特别想问的是,测试隐式解包选项与零和可选绑定相比,在技术上的缺点。

我所希望的是可以量化的,为什么一个比另一个更好的技术原因(即编译器优化、更好的安全检查、性能等等),换句话说,而不仅仅是一个“嘿,因为这不是‘快速’的方式!”希望这是有道理的。

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

此外,在括号内还有另一个作用域,它也可以有一个名为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 
}

再说一次,我不是在宽恕这一点!!我只是展示了一些其他人可能会从有益的角度发现有趣的代码。我确实做到了!

共有3个答案

郭麒
2023-03-14

让我们明确IUO的真正目的。它们的起源纯粹是为了与Objective-C进行通信。从理论上讲,任何来自Objective-C的对象都可能是nil,因此在一开始,每个来自Objective-C的对象都是可选的。让每一个这样的可选对象都成为一个真正的可选对象太过混乱,所以每一个这样的对象都是一个隐式展开的可选对象。

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

在这种情况下,实际上根本不需要隐式展开选项。在这一点上,它们只不过是程序员的懒惰便利。其他语言设备的出现使它们变得不必要。例如,如果let使用展开可选并被迫添加级别花括号是很痛苦的,但是现在我们有保护let,因此您可以在不添加级别花括号的情况下展开。

因此,苹果开始拧紧螺丝,使得IUO越来越不方便。例如,正如瓦卡瓦马正确地说的那样,它们不再通过分配来传播。而且不再有IUO数组这样的东西(例如,[Int!] 不再是一种类型)。

这一过程将继续下去,直到很少有IUO是合法的。因此,最好现在就改掉使用它们的习惯。

单于皓轩
2023-03-14

由于在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 '?'?
}

如果改为使用字符串并将其展开,则代码在Swift 2中都能正常工作。x和Swift 3:

let stringA: String? = "Hello"

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

请参阅此处对此更改的官方讨论:SE-0054:废除隐含未包装可选类型

本文件的动机部分陈述了[我的重点补充]:

隐式WrappedOptional(“IUO”)类型是导入Objective-C API的一个有价值的工具,其中未指定参数或返回类型的可空性。它还代表了一种方便的机制,用于解决初始化器中的确定初始化问题。然而,IUO是一种过渡技术;它们代表了一种简单的方法,可以绕过未注释的API,或者缺少能够更优雅地处理特定代码模式的语言特性。因此,我们希望进一步限制它们的使用,并引入更具体的语言特性来取代它们。除了一些特定的情况外,期权始终是更安全的赌注,我们希望鼓励人们使用期权,而不是IUO。

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

这表明语言设计者认为应该尽可能少地使用隐式展开选项。

秦焱
2023-03-14

确实有一个简单的原因,它是您列出的原因之一:“更好的安全检查”。可选绑定保留编译器已知的展开范围,而程序员有责任跟踪您何时检查了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属于这一类:您可以相信它们有一个值,并且您不想一直将它们展开,但它们不是在init中指定的,因此必须将它们设置为可选。这就是隐式展开选项的用途。

解包装到同名变量

你的更新尝试性地建议将可选项展开为同名变量,实际上是一种出色的Swift风格,苹果公司早就经常使用它。当您展开到同名变量时,代码更容易阅读,因为您可以跟踪更少(更好)的名称。绝对要接受这个!

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.
}
 类似资料:
  • 问题内容: 我正在使用Xcode 6 Beta4。我有这种奇怪的情况,我无法弄清楚如何适当地测试可选项。 如果我有可选的xyz,则是正确的测试方法: 要么 这些文档说是第一种方法,但是我发现有时第二种方法是必需的,并且不会生成编译器错误,但是有时,第二种方法会生成编译器错误。 我的具体示例是使用桥接到swift的GData XML解析器: 在这里,如果我刚做过: 它将始终返回true。但是,如果我

  • 在Swift 4.0中,以下代码不可编译: 现在我想象是

  • 请原谅我的无知,我恐怕错过了一些基本的选项理解。我的印象是(隐式展开的可选指示符)保证该类型的变量不为零。然而,这个非常简单的Apple API很少会返回我。 这是一个意外的错误还是Optionals规范的一部分?因为如果这是规范的一部分,我不明白为什么会有选项,而不是变量可以存在或。

  • 如果像Apple在Swift编程中所说的隐式展开选项应该总是有一个值,那么为什么不使用非选项来代替呢?我知道非选项不能赋值为nil,但还有其他区别吗?

  • 问题内容: 快速使用以下语法进行流控制 在这种情况下 ,真值上下文的语义是什么 ? 是否允许 表达式链接 (如下所示)? 如果是这样,布尔表达式是否会短路? 问题答案: 首先检查它是否为零或是否有数据。如果为零,则不会执行if语句。如果有数据,则将数据解包并分配给if语句的范围。然后执行括号内的代码。 无法在一个if语句中链接此功能。不直接评估为布尔值。最好将“ if let”视为一个特殊关键字。

  • 问题内容: 我正在尝试确定给定类型()是否为可选类型,我正在使用此测试 但它始终返回false。 那么有什么办法可以做到这一点? 问题答案: 假设您要执行的操作是这样的: 可悲的是,swift当前(从Swift 2开始)不支持协方差和协变,并且不能直接针对类型进行类型检查: 一种替代方法是使特定协议扩展,并检查该类型: