Swift:测试nil的可选项


137

我正在使用Xcode 6 Beta4。我有这种奇怪的情况,我无法弄清楚如何适当地测试可选项。

如果我有可选的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以及它处理我缺少的可选内容的方式吗?


4
我们能为您的意外情况和错误情况看到完整的示例吗?(而且我知道这很困难,但请尝试开始失去附加条件周围的括号!)
Matt Gibson 2014年

1
这在Xcode 6 beta 5中有所更改
newacct 2014年


我刚刚用意外的情况更新了问题。
tng

我尚未更新到Beta 5。我会尽快做到的;很高兴见到 ??在Swift中,以及更一致的可选行为。
tng

Answers:


172

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

5
有人可以详细说明为什么xyz == nil不起作用吗?
克里斯多夫

第二个示例应显示为://使用xy做某事(这是两个变体之间用例的主要区别)
Alper

@Christoph:错误消息是在初始化之前使用了常量“ xyz”。我认为您需要在xyz声明行的末尾附加'= nil'。
user3207158

对于那些像我这样新手并想知道的人:您仍然必须在if块中解包xyz。即使您强制解开也是安全的
Omar

@Omar这就是第二种形式的优点,您可以在块中使用xy而不用拆开它。
Erik Doernenburg

24

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

var a: Int? = 5

if let b = a {
   // do something
}

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

var a: Int? = 5

if let a = a {
    // do something
}

这可以帮助您避免耗尽创意变量名...

这利用了Swift支持的可变阴影


18

使用可选选项最直接的方法之一是:

假设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。在将其视为一个变量之前,请确保要使用的变量是可选变量。


1
我发现这种方法很不好,因为我不必要地创建一个新变量(常量),而不得不创建一个新名称,大多数情况下,新名称比前一个更糟。花了我更多的时间写给读者。
隆巴

3
这是包装可选变量的标准方法和推荐方法。“'if let”非常强大。
gnasher729

@ gnasher729但是如果您只想使用else部分,该怎么办
Brad Thomas

15

雨燕3.0,4.0

检查nil可选的方式主要有两种。这是它们之间比较的示例

1.如果让

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

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

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

}

2.守卫

guard let可以做类似的事情。它的主要目的是使其在逻辑上更加合理。就像说确保变量不是nil,否则请停止该函数guard let也可以做额外的条件检查if let

区别在于,展开的值将在与相同的范围内可用guard let,如下面的注释所示。这也导致在其他封闭,该程序必须退出当前范围来看,通过returnbreak等等。

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

11

从快速编程指南

如果语句和强制展开

您可以使用if语句来找出可选值是否包含值。如果可选参数确实具有值,则其值为true;否则为true。如果它根本没有任何值,则结果为false。

所以最好的方法是

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

并且,如果您使用的是xyzin if语句。则可以xyz在常量变量中解开if语句。因此,您无需解开xyz使用if语句where的所有位置。

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

该约定由提出,apple并将由开发者遵循。


2
在Swift 3中,您必须做if xyz != nil {...}
psmythirl

在Swift 3中,可选参数不能用作布尔值。首先是因为它是一种C-ism,首先不应该出现在该语言中,其次是因为它与可选的Bool造成了绝对的混淆。
gnasher729

9

尽管您仍然必须显式地比较可选对象与nil或使用可选绑定来额外提取其值(即,可选对象不会隐式转换为布尔值),但值得注意的是,Swift 2已添加该guard语句以帮助避免工作时的厄运金字塔具有多个可选值。

换句话说,您的选择现在包括显式检查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

1
如上所述,您也可以这样做以摆脱嵌套的可选statmenets。如果让abc = abc?.xyz?.something {}
Suhaib 16/09/23

1
@Suhaib啊,是的:您还可以使用可选的链接(developer.apple.com/library/prerelease/content/documentation/…)快速访问嵌套属性。我在这里没有提到它,因为我认为这可能与原始问题相去甚远。
克里斯·弗雷德里克

好答案!但是Swift,OMG ...距离程序员友好还有很长的路要走!
turingtest

@turingtested谢谢!实际上,从确保您不会意外尝试使用值的角度来看,我认为这对程序员友好的nil,但是我同意学习曲线有些陡峭。:)
克里斯·弗雷德里克

4

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
}

我对这个答案有些不满意,想知道为什么。至少要发表评论,如果您要拒绝投票。
Brody Robertson

1
可能是尝试将语言保持某种形式的pythonic pure 的swift类似物pythonistas。我拿 我只是将您的代码放入Utils mixin中。
javadba


2

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

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

xyz == nil表达式无效,但x != nil有效。不知道为什么。
安德鲁

2

除了使用ifor guard语句来执行可选绑定之外,另一种方法是扩展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
}

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


2

一个尚未明确涵盖的选项是使用Swift的被忽略的值语法:

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

我喜欢这种方式,因为nil在像Swift这样的现代语言中检查感觉是否不合适。我认为感觉不合时宜的原因是,这nil基本上是定点价值。在现代编程的其他任何地方,我们几乎都没有使用哨兵,所以nil感觉它也应该消失。


1

你也可以用 Nil-Coalescing Operator

nil-coalescing operatora ?? b)解开一个可选a,如果它包含a值,或返回a默认值b如果anil。表达式a始终是可选类型。表达式b必须与内部存储的类型匹配a

let value = optionalValue ?? defaultValue

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


我喜欢它,因为它提供了简单的选项来指定默认值。
thowa

0
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.")     }

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


2
OP关注的情况是:if (xyz != nil)
zaph 2014年

0

如果您有条件并且想展开和比较,那么如何利用复合布尔表达式的短路求值方法,如下所示:

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

}

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.