ChatGPT解决这个技术问题 Extra ChatGPT

Swift:测试 nil 的可选项

我正在使用 Xcode 6 Beta 4。我遇到了这种奇怪的情况,我无法弄清楚如何适当地测试选项。

如果我有一个可选的 xyz,那么正确的测试方法是:

if (xyz) // Do something

或者

if (xyz != nil) // Do something

文件说要采用第一种方式,但我发现有时需要第二种方式,并且不会产生编译器错误,但其他时候,第二种方式会产生编译器错误。

我的具体示例是使用桥接到 swift 的 GData XML 解析器:

let xml = GDataXMLDocument(
    XMLString: responseBody,
    options: 0,
    error: &xmlError);

if (xmlError != nil)

在这里,如果我只是这样做:

if xmlError

它总是会返回 true。但是,如果我这样做:

if (xmlError != nil)

然后它就可以工作了(就像它在 Objective-C 中的工作方式一样)。

GData XML 以及它处理我缺少的选项的方式有什么问题吗?

请给我们看一个关于您的意外情况和错误情况的完整示例吗? (而且我知道这很难,但试着开始去掉条件句周围的括号!)
这在 Xcode 6 beta 5 中有所改变
我刚刚用意外情况更新了这个问题。
我还没有更新到 beta 5。我很快就会这样做;很高兴见到 ??在 Swift 中,以及更一致的可选行为。

p
paulmelnikow

在 Xcode Beta 5 中,它们不再允许您这样做:

var xyz : NSString?

if xyz {
  // Do something using `xyz`.
}

这会产生一个错误:

不符合协议'BooleanType.Protocol'

您必须使用以下形式之一:

if xyz != nil {
   // Do something using `xyz`.
}

if let xy = xyz {
   // Do something using `xy`.
}

有人可以详细说明为什么 xyz == nil 不起作用?
第二个示例应为: // 使用 xy 执行某些操作(这是两个变体在用例中的主要区别)
@Christoph:错误消息是在初始化之前使用的常量'xyz'。我认为您需要在 xyz 的声明行末尾附加“= nil”。
对于那些像我一样不熟悉 swift 并且想知道的人:您仍然必须在 if 块中解开 xyz。虽然它是安全的,即使你强制打开
@Omar 这就是第二种形式的美妙之处,您可以在块内使用 xy 而无需打开它。
F
Flimm

要添加到其他答案,而不是在 if 条件内分配给不同命名的变量:

var a: Int? = 5

if let b = a {
   // do something
}

您可以像这样重用相同的变量名:

var a: Int? = 5

if let a = a {
    // do something
}

这可能会帮助您避免用完创意变量名称...

这利用了 Swift 支持的 variable shadowing


I
Isaac Drachman

使用选项的最直接方法之一如下:

假设 xyz 是可选类型,例如 Int?

if let possXYZ = xyz {
    // do something with possXYZ (the unwrapped value of xyz)
} else {
    // do something now that we know xyz is .None
}

这样您就可以测试 xyz 是否包含一个值,如果是,则立即使用该值。

关于您的编译器错误,类型 UInt8 不是可选的(注意没有“?”),因此不能转换为 nil。在将其视为一个变量之前,请确保您正在使用的变量是一个可选变量。


我发现这种方法很糟糕,因为我不必要地创建了一个新变量(常量),并且不得不创建一个新名称,大多数时候它会比以前更糟糕。花费我更多的时间来写作和为读者服务。
这是解开可选变量的标准方法和推荐方法。 “'if let”非常强大。
@gnasher729 但是如果您只想使用 else 部分怎么办
F
Fangming

斯威夫特 3.0、4.0

主要有两种检查可选项是否为 nil 的方法。以下是它们之间的比较示例

1.如果让

if let 是检查可选项是否为 nil 的最基本方法。其他条件可以附加到这个 nil 检查,用逗号分隔。变量不能为 nil 才能移动到下一个条件。如果只需要 nil 检查,请删除以下代码中的额外条件。

除此之外,如果 x 不为零,则将执行 if 闭包并且 x_val 将在内部可用。否则触发 else 闭包。

if let x_val = x, x_val > 5 {
    //x_val available on this scope
} else {

}

2.守护让

guard let 可以做类似的事情。它的主要目的是使它在逻辑上更合理。这就像说确保变量不为零,否则停止函数guard let 还可以像 if let 一样进行额外的条件检查。

不同之处在于展开的值将在与 guard let 相同的范围内可用,如下面的评论所示。这也导致在 else 闭包中,程序必须通过 returnbreak 等退出当前范围。

guard let x_val = x, x_val > 5 else {
    return
}
//x_val available on this scope

I
Iulian Onofrei

来自 swift 编程guide

If 语句和强制解包 您可以使用 if 语句来查明可选项是否包含值。如果一个可选项确实有一个值,它的计算结果为真;如果它根本没有价值,它的评估结果为假。

所以最好的方法是

// swift > 3
if xyz != nil {}

如果您在 if 语句中使用 xyz。那么您可以在常量变量中的 if 语句中解开 xyz。因此您不需要解开 if 语句中使用 xyz 的每个地方。

if let yourConstant = xyz {
      //use youtConstant you do not need to unwrap `xyz`
}

apple 建议使用此约定,开发人员将遵循此约定。


在 Swift 3 中,您必须执行 if xyz != nil {...}
在 Swift 3 中,选项不能用作布尔值。首先是因为它是一种 C 主义,本来就不应该出现在语言中,其次因为它会导致与可选 Bool 的绝对混淆。
C
Chris Frederick

尽管您仍然必须显式地将可选项与 nil 进行比较,或者使用可选项绑定来额外提取其值(即,可选项不会隐式转换为布尔值),但值得注意的是 Swift 2 添加了 guard statement 来帮助避免pyramid of doom 使用多个可选值时。

换句话说,您的选项现在包括显式检查 nil

if xyz != nil {
    // Do something with xyz
}

可选绑定:

if let xyz = xyz {
    // Do something with xyz
    // (Note that we can reuse the same variable name)
}

guard 语句:

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    // e.g. by calling return, break, continue, or throw
    return
}

// Do something with xyz, which is now guaranteed to be non-nil

请注意,当有多个可选值时,普通的可选绑定如何导致更大的缩进:

if let abc = abc {
    if let xyz = xyz {
        // Do something with abc and xyz
    }        
}

您可以使用 guard 语句避免这种嵌套:

guard let abc = abc else {
    // Handle failure and then exit this code block
    return
}

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    return
}

// Do something with abc and xyz

如上所述,您也可以这样做以摆脱嵌套的可选状态。如果让 abc = abc?.xyz?.something { }
@Suhaib 啊,是的:您还可以使用可选链接 (developer.apple.com/library/prerelease/content/documentation/…) 来快速访问嵌套属性。我在这里没有提到它,因为我认为它可能与原始问题相去甚远。
@turingtested 谢谢!实际上,我认为这个 对程序员很友好,因为它保证您不会意外尝试使用 nil 值,但我同意学习曲线有点陡峭。 :)
B
Brody Robertson

Swift 5 协议扩展

这是一种使用协议扩展的方法,以便您可以轻松地内联可选的 nil 检查:

import Foundation

public extension Optional {

    var isNil: Bool {

        guard case Optional.none = self else {
            return false
        }

        return true

    }

    var isSome: Bool {

        return !self.isNil

    }

}

用法

var myValue: String?

if myValue.isNil {
    // do something
}

if myValue.isSome {
    // do something
}

它可能是 pythonistasswift 类似物,它试图使语言保持某种形式的 Pythonic 纯度。我的看法?我刚刚将您的代码提升到我的 Utils mixin 中。
B
Ben Lachman

没有特别提到的一种选择是使用 Swift 的忽略值语法:

if let _ = xyz {
    // something that should only happen if xyz is not nil
}

我喜欢这样,因为在 Swift 这样的现代语言中检查 nil 感觉格格不入。我认为它感觉不合适的原因是 nil 基本上是一个哨兵值。在现代编程中,我们几乎在其他任何地方都取消了哨兵,所以 nil 感觉也应该这样做。


q
qed

当您想根据某物是否为 nil 来获取值时,三元运算符可能会派上用场,而不是 if

func f(x: String?) -> String {
    return x == nil ? "empty" : "non-empty"
}

xyz == nil 表达式不起作用,但 x != nil 有效。不知道为什么。
T
Tiago

除了使用 ifguard 语句进行可选绑定之外,另一种方法是扩展 Optional

extension Optional {

    func ifValue(_ valueHandler: (Wrapped) -> Void) {
        switch self {
        case .some(let wrapped): valueHandler(wrapped)
        default: break
        }
    }

}

ifValue 接收一个闭包,并在可选项不为 nil 时将其作为参数调用。它是这样使用的:

var helloString: String? = "Hello, World!"

helloString.ifValue {
    print($0) // prints "Hello, World!"
}

helloString = nil

helloString.ifValue {
    print($0) // This code never runs
}

您可能应该使用 ifguard,因为它们是 Swift 程序员使用的最传统(因此很熟悉)的方法。


y
yoAlex5

可选的

您也可以使用 Nil-Coalescing Operator

nil-coalescing 运算符 (a ?? b) 如果可选 a 包含一个值,则解包它,如果 a 为 nil,则返回默认值 b。表达式 a 始终是可选类型。表达式 b 必须匹配存储在 a 中的类型。

let value = optionalValue ?? defaultValue

如果 optionalValuenil,它会自动将值分配给 defaultValue


我喜欢这样,因为它提供了指定默认值的简单选项。
N
Nicolas Manzini

现在您可以快速执行以下操作,让您重新获得一点目标-c if nil else

if textfieldDate.text?.isEmpty ?? true {

}

M
Mundi
var xyz : NSDictionary?

// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary() 
// case 3: do nothing

if xyz { NSLog("xyz is not nil.") }
else   { NSLog("xyz is nil.")     }

该测试在所有情况下都按预期工作。顺便说一句,您不需要括号 ()


OP 关注的情况是:if (xyz != nil)
R
RT Denver

如果您有条件并且想要展开和比较,那么如何利用复合布尔表达式的短路评估,如

if xyz != nil && xyz! == "some non-nil value" {

}

当然,这不像其他一些建议的帖子那样可读,但可以完成工作并且比其他建议的解决方案更简洁。


T
Tob

如果有人也尝试使用字典并尝试使用 Optional(nil)。

 let example : [Int:Double?] = [2: 0.5]
 let test = example[0]

你最终会得到 Double?? 类型。

要继续处理您的代码,只需使用合并来绕过它。

 let example : [Int:Double?] = [2: 0.5]
 let test = example[0] ?? nil

现在你只有双?

这完全合乎逻辑,但我搜索了错误的东西,也许它可以帮助别人。