简体   繁体   English

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

[英]Is there a technical downside to using implicitly-unwrapped optionals but testing for nil vs optional binding?

Okay, so I know the normal way to use optionals in Swift is via optional bindings to unwrap them, like so... 好的,所以我知道在Swift中使用选项的正常方法是通过可选的绑定来解开它们,就像这样......

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

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

But I've also seen the following way using implicitly-unwrapped optionals, which I personally think looks cleaner as it not only doesn't require an extra variable, but it also 'reads' better and more like English (ie 'If this is not nil, then...') and readability is one of the core tenets of Swift (especially in the upcoming Swift 3.0). 但我也看到了使用隐式解包的选项的以下方式,我个人认为它看起来更干净,因为它不仅不需要额外的变量,而且它“读取”更好,更像英语(即'如果这是不是,然后......')可读性是Swift的核心原则之一(特别是在即将推出的Swift 3.0中)。

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

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

However, in regards to the latter, Swift 'purists' refer to this as 'code smell' and insist it's 'Bad, bad, bad!!'... but they never explain why! 然而,就后者而言,斯威夫特的“纯粹主义者”将此称为“代码嗅觉”并坚持认为它是“坏,坏,坏!!”......但他们从未解释过为什么! So... why is it so bad? 那么......为什么这么糟糕?

Note: Yes, I know about Optional chaining and other such features which you can't use with implicitly-unwrapped optionals, and they are all really cool features, but I am specifically asking about the technical downside to testing implicitly-unwrapped optionals against nil vs optional binding. 注意:是的,我知道可选链接和其他这些功能,你不能使用隐式解包的选项,它们都是非常酷的功能,但我特别询问测试隐式解包的选项对nil的技术缺点vs可选绑定。

What I'm hoping for are quantifiable, technical reasons why one is better than the other (ie compiler optimizations, better safety checking, performance, etc.) In other words, not just a 'Hey, because that's not the 'Swifty' way to do it!' 我希望的是可量化的技术原因,为什么一个比另一个好(即编译器优化,更好的安全检查,性能等)换句话说,不仅仅是'嘿,因为那不是'Swifty'的方式去做吧!' Hope that makes sense. 希望有道理。

Update 更新

I actually just found a way to address one of my concerns (at least in a superficial way) which is having to create a new variable to hold the unwrapped one. 我实际上刚刚找到了一种方法来解决我的一个问题(至少是表面的方式),它必须创建一个新变量来保存未包装的变量。 The trick here is since the unwrapped variable is actually in a different scope than the optional itself, you can use the exact same name. 这里的技巧是因为展开的变量实际上与可选的本身不同,你可以使用完全相同的名称。

Further still, inside the brackets themselves is yet another scope which can also have a variable named stringA. 此外,括号内部还有另一个范围,它也可以有一个名为stringA的变量。 This means you can now have three 'stringA' variables (but doesn't mean you should! )... 这意味着你现在可以拥有三个'stringA'变量(但并不意味着你应该! )......

  1. The optional 可选的
  2. The unwrapped optional 打开的可选项
  3. A new, local variable inside the brackets 括号内的新的局部变量

Here's this crazy code showing this... 这是这个疯狂的代码显示这个......

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 
}

Again, I am not condoning this!! 再一次,我不是在纵容这个!! I'm just showing some code that others may find interesting from an instructive perspective. 我只是从一个有启发性的角度展示其他人可能会感兴趣的代码。 I sure did! 我确定做到了!

There is indeed a simple reason, and it's one of the ones you listed: "better safety checking". 确实有一个简单的原因,这是你列出的一个:“更好的安全检查”。 Optional bindings keep the scope of the unwrap known to the compiler, whereas it's the programmer's responsibility to keep track of when you have or have not checked an implicitly unwrapped optional for nil . 可选绑定保持编译器知道的解包的范围,而这是程序员的责任时保留已经被或尚未检查的隐含展开可选的跟踪nil

With optional bindings: 使用可选绑定:

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

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

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

With implicit unwrapping: 隐式展开:

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

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

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

What are IUOs good for? 什么 IUO有益?

Why have implicitly unwrapped optionals at all, then? 为什么要隐含地打开选项呢? They exist for basically one purpose: properties that will definitely have a value, but not until slightly after init , so they have to be optional. 它们的存在基本上只有一个目的:肯定有一个值的属性,但直到init之后才有,所以它们必须是可选的。 Often @IBOutlet s fall into this category: you can trust them to have a value, and you don't want to be unwrapping them all the time, but they are not assigned at init , so you must make them optional. @IBOutlet经常属于这个类别:你可以相信它们有一个值,你不想一直打开它们,但它们不是在init分配的,所以你必须使它们成为可选的。 That is what implicitly unwrapped optionals are for. 就是隐含的解包方案。

Unwrapping to a variable of the same name 展开到同名变量

Your update, which tentatively suggests unwrapping optionals into variables with the same name, is actually excellent Swift style, and has been used by Apple early and often. 您的更新暂时建议将选项包装到具有相同名称的变量中,实际上是优秀的 Swift风格,并且早期和经常被Apple使用。 Code is easier to read when you unwrap to variables of the same name, because you can track fewer (and better) names. 当您打开同名变量时,代码更容易阅读,因为您可以跟踪更少(和更好)的名称。 Definitely embrace this! 绝对拥抱这个!

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.
}

Due to a change to implicitly unwrapped optionals in Swift 3 , you will be much happier if you properly unwrap regular optionals instead of using implicitly unwrapped optionals. 由于Swift 3中隐式解包选项更改,如果您正确解包常规选项而不是使用隐式解包的选项,您会更高兴。

In Swift 3 , if you assign a String! Swift 3中 ,如果你指定一个String! to a new variable, that variable will have the type String? 到一个新变量,该变量将具有String?类型String? . This means your implicitly unwrapped optional becomes a regular optional on assignment, and you now have to deal with unwrapping the new variable. 这意味着您隐式展开的可选项在赋值时变为常规可选项,您现在必须处理展开新变量的问题。

This code works in Swift 2.x : 此代码适用于Swift 2.x

let stringA: String! = "Hello"

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

In Swift 3 : 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 '?'?
}

If instead you use a String? 相反,你使用String? and unwrap it, then the code works as expected in both Swift 2.x and Swift 3 : 并解开它,然后代码在Swift 2.xSwift 3中按预期工作:

let stringA: String? = "Hello"

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

See the official discussion of this change here: SE-0054: Abolish ImplicitlyUnwrappedOptional Type 请参阅此处有关此更改的官方讨论: SE-0054:废除隐式解包的可选类型

The Motivation section of this document states [emphasis added mine]: 本文件的“ 动机”部分指出[强调添加我的]:

The ImplicitlyUnwrappedOptional ("IUO") type is a valuable tool for importing Objective-C APIs where the nullability of a parameter or return type is unspecified. ImplicitlyUnwrappedOptional(“IUO”)类型是用于导入Objective-C API的有用工具,其中未指定参数或返回类型的可为空性。 It also represents a convenient mechanism for working through definite initialization problems in initializers. 它还代表了一种在初始化器中处理明确初始化问题的便捷机制。 However, IUOs are a transitional technology; 但是,IUO是一种过渡技术; they represent an easy way to work around un-annotated APIs, or the lack of language features that could more elegantly handle certain patterns of code. 它们代表了 解决未注释API 的简单 方法,或者缺乏 可以更优雅地处理某些代码模式的 语言功能 As such, we would like to limit their usage moving forward, and introduce more specific language features to take their place. 因此,我们 希望限制其使用前进,并引入更 具体的语言功能来取代它们。 Except for a few specific scenarios, optionals are always the safer bet, and we'd like to encourage people to use them instead of IUOs. 除了一些 特定情况,选项总是更安全的赌注,我们希望 鼓励人们使用它们而不是IUO。

This proposal seeks to limit the adoption of IUOs to places where they are actually required, and put the Swift language on the path to removing implicitly unwrapped optionals from the system entirely when other technologies render them unnecessary. 该提案旨在将IUO的采用限制在 实际需要 它们的地方 ,并且 其他技术不必要 ,将Swift语言放在 完全从系统中删除隐式未包装选项 的路径上 It also completely abolishes any notion of IUOs below the type-checker level of the compiler, which will substantially simplify the compiler implementation. 它还完全废除了IUO的任何概念,低于编译器的类型检查器级别,这将大大简化编译器实现。

This makes it clear that the language designers believe that you should use Implicitly Unwrapped Optionals as little as possible. 这清楚地表明语言设计者认为你应该尽可能少使用Implicitly Unwrapped Optionals

Let's be clear on the real purpose of IUOs. 让我们清楚IUO的真正目的。 They originated purely as a device for communication with Objective-C. 它们纯粹是作为与Objective-C进行通信的设备而发明的。 Any object coming from Objective-C might be nil , in theory, so in the beginning every object coming from Objective-C was an Optional. 在理论上,来自Objective-C的任何对象可能都是nil ,所以在开始时,来自Objective-C的每个对象都是可选的。 Having every such Optional be a true Optional was too disruptive, so such every object was an implicitly unwrapped Optional. 让每个这样的Optional都是一个真正的Optional是太具有破坏性了,所以这样的每个对象都是一个隐式解包的Optional。

Then, however, Apple started hand-tweaking the APIs to tell Swift whether an object coming from Objective-C could in fact be nil . 然而,Apple开始手动调整API以告诉Swift来自Objective-C的对象实际上是否nil That hand-tweaking process is just about finished now (Swift 3). 手动调整过程即将完成(Swift 3)。 Thus, everything coming from Objective-C is now either a normal object or a normal Optional (or, in some cases, a normal object that you have to pass thru a try to obtain). 因此,来自Objective-C的所有内容现在都是普通对象或普通可选项(或者,在某些情况下,您必须通过try获取的普通对象)。

This being so, there is actually no need at all for implicitly unwrapped Optionals. 既然如此,实际上根本没有需要隐式展开的Optionals。 They are nothing but a lazy convenience for programmers at this point. 在这一点上,它们只是程序员的懒惰。 And other linguistic devices have come along to make them unnecessary. 还有其他语言设备使它们变得不必要。 For example, it is a pain to unwrap an Optional with if let and be forced to add a level curly braces, but now we have guard let so you can unwrap without adding a level of curly braces. 例如, if let和被迫添加一个级别花括号,解开一个Optional是一件痛苦的事情,但是现在我们有guard let你可以解开而不添加一个花括号。

Apple has therefore started to tighten the screws, making IUOs less and less convenient. 因此,Apple开始拧紧螺丝,使IUO变得越来越不方便。 For example, as vacawama has rightly said, they no longer propagate by assignment. 例如,正如vacawama正确地说的那样,他们不再通过任务传播。 And there is no longer any such thing as an Array of IUOs (eg, [Int!] is no longer a type). 并且不再存在IUO数组(例如, [Int!]不再是类型)。

This process will continue until there are few if any contexts in which an IUO is legal. 这个过程将持续到IUO合法的情况很少(如果有的话)为止。 It is thus best to get out of the habit of using them now . 因此,最好摆脱现在使用它们的习惯。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM