简体   繁体   English

Swift:测试 nil 的可选项

[英]Swift: Testing optionals for nil

I'm using Xcode 6 Beta 4. I have this weird situation where I cannot figure out how to appropriately test for optionals.我正在使用 Xcode 6 Beta 4。我遇到了这种奇怪的情况,我无法弄清楚如何适当地测试选项。

If I have an optional xyz, is the correct way to test:如果我有一个可选的 xyz,那么正确的测试方法是:

if (xyz) // Do something

or或者

if (xyz != nil) // Do something

The documents say to do it the first way, but I've found that sometimes, the second way is required, and doesn't generate a compiler error, but other times, the second way generates a compiler error.文件说要采用第一种方式,但我发现有时需要第二种方式,并且不会产生编译器错误,但其他时候,第二种方式会产生编译器错误。

My specific example is using the GData XML parser bridged to swift:我的具体示例是使用桥接到 swift 的 GData XML 解析器:

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

if (xmlError != nil)

Here, if I just did:在这里,如果我只是这样做:

if xmlError

it would always return true.它总是会返回 true。 However, if I do:但是,如果我这样做:

if (xmlError != nil)

then it works (as how it works in Objective-C).然后它就可以工作了(就像它在 Objective-C 中的工作方式一样)。

Is there something with the GData XML and the way it treats optionals that I am missing? GData XML 以及它处理我缺少的选项的方式有什么问题吗?

In Xcode Beta 5, they no longer let you do:在 Xcode Beta 5 中,它们不再允许您这样做:

var xyz : NSString?

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

This produces an error:这会产生一个错误:

does not conform to protocol 'BooleanType.Protocol'不符合协议'BooleanType.Protocol'

You have to use one of these forms:您必须使用以下形式之一:

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

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

To add to the other answers, instead of assigning to a differently named variable inside of an if condition:要添加到其他答案,而不是在if条件内分配给不同命名的变量:

var a: Int? = 5

if let b = a {
   // do something
}

you can reuse the same variable name like this:您可以像这样重用相同的变量名:

var a: Int? = 5

if let a = a {
    // do something
}

This might help you avoid running out of creative variable names...这可能会帮助您避免用完创意变量名称...

This takes advantage of variable shadowing that is supported in Swift.这利用了 Swift 支持的变量阴影

One of the most direct ways to use optionals is the following:使用选项的最直接方法之一如下:

Assuming xyz is of optional type, like Int?假设xyz是可选类型,比如Int? for example.例如。

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

This way you can both test if xyz contains a value and if so, immediately work with that value.这样您就可以测试xyz是否包含一个值,如果是,则立即使用该值。

With regards to your compiler error, the type UInt8 is not optional (note no '?') and therefore cannot be converted to nil .关于您的编译器错误,类型UInt8不是可选的(注意没有'?'),因此不能转换为nil Make sure the variable you're working with is an optional before you treat it like one.在将其视为一个变量之前,请确保您正在使用的变量是一个可选变量。

Swift 3.0, 4.0斯威夫特 3.0、4.0

There are mainly two ways of checking optional for nil.主要有两种检查可选项是否为 nil 的方法。 Here are examples with comparison between them以下是它们之间的比较示例

1. if let 1.如果让

if let is the most basic way to check optional for nil. if let是检查可选项是否为 nil 的最基本方法。 Other conditions can be appended to this nil check, separated by comma.其他条件可以附加到这个 nil 检查,用逗号分隔。 The variable must not be nil to move for the next condition.变量不能为 nil 才能移动到下一个条件。 If only nil check is required, remove extra conditions in the following code.如果只需要 nil 检查,请删除以下代码中的额外条件。

Other than that, if x is not nil, the if closure will be executed and x_val will be available inside.除此之外,如果x不是 nil,则将执行 if 闭包并且x_val将在内部可用。 Otherwise the else closure is triggered.否则触发 else 闭包。

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

}

2. guard let 2.守护让

guard let can do similar things. guard let可以做类似的事情。 It's main purpose is to make it logically more reasonable.它的主要目的是使它在逻辑上更合理。 It's like saying Make sure the variable is not nil, otherwise stop the function .这就像说确保变量不为零,否则停止函数 guard let can also do extra condition checking as if let . guard let也可以像if let一样进行额外的条件检查。

The differences are that the unwrapped value will be available on same scope as guard let , as shown in the comment below.不同之处在于,未包装的值将在与guard let相同的范围内可用,如下面的评论所示。 This also leads to the point that in else closure, the program has to exit the current scope, by return , break , etc.这也导致在 else 闭包中,程序必须通过returnbreak等退出当前范围。

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

From swift programming guide来自 swift 编程指南

If Statements and Forced Unwrapping If 语句和强制展开

You can use an if statement to find out whether an optional contains a value.您可以使用 if 语句来确定可选项是否包含值。 If an optional does have a value, it evaluates to true;如果一个可选项确实有一个值,它的计算结果为真; if it has no value at all, it evaluates to false.如果它根本没有价值,它的评估结果为假。

So the best way to do this is所以最好的方法是

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

and if you are using the xyz in if statement.Than you can unwrap xyz in if statement in constant variable .So you do not need to unwrap every place in if statement where xyz is used.如果你在 if 语句中使用xyz 。那么你可以在常量变量的 if 语句中解开xyz 。所以你不需要解开 if 语句中使用xyz的每个地方。

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

This convention is suggested by apple and it will be followed by devlopers.这个约定是apple建议的,开发者也会遵循的。

Although you must still either explicitly compare an optional with nil or use optional binding to additionally extract its value (ie optionals are not implicitly converted into Boolean values), it's worth noting that Swift 2 has added the guard statement to help avoid the pyramid of doom when working with multiple optional values.尽管您仍然必须显式地将可选项与nil进行比较,或者使用可选项绑定来额外提取其值(即可选项不会隐式转换为布尔值),但值得注意的是 Swift 2 添加了guard语句以帮助避免厄运金字塔使用多个可选值时。

In other words, your options now include explicitly checking for nil :换句话说,您的选项现在包括显式检查nil

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

Optional binding:可选绑定:

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

And guard statements: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

Note how ordinary optional binding can lead to greater indentation when there is more than one optional value:请注意,当有多个可选值时,普通的可选绑定如何导致更大的缩进:

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

You can avoid this nesting with guard statements:您可以使用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

Swift 5 Protocol Extension Swift 5 协议扩展

Here is an approach using protocol extension so that you can easily inline an optional nil check:这是一种使用协议扩展的方法,以便您可以轻松地内联可选的 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

    }

}

Usage用法

var myValue: String?

if myValue.isNil {
    // do something
}

if myValue.isSome {
    // do something
}

One option that hasn't specifically been covered is using Swift's ignored value syntax:没有特别提到的一种选择是使用 Swift 的忽略值语法:

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

I like this since checking for nil feels out of place in a modern language like Swift.我喜欢这样,因为在 Swift 这样的现代语言中检查nil感觉格格不入。 I think the reason it feels out of place is that nil is basically a sentinel value.我认为它感觉不合适的原因是nil基本上是一个哨兵值。 We've done away with sentinels pretty much everywhere else in modern programming so nil feels like it should go too.在现代编程中,我们几乎在其他任何地方都取消了哨兵,所以nil感觉它也应该消失。

Instead of if , ternary operator might come handy when you want to get a value based on whether something is nil:当您想根据某物是否为 nil 来获取值时,三元运算符可能会派上用场,而不是if

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

Another approach besides using if or guard statements to do the optional binding is to extend Optional with:除了使用ifguard语句来进行可选绑定之外,另一种方法是扩展Optional

extension Optional {

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

}

ifValue receives a closure and calls it with the value as an argument when the optional is not nil. ifValue接收一个闭包,并在可选项不为 nil 时以该值作为参数调用它。 It is used this way:它是这样使用的:

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

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

helloString = nil

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

You should probably use an if or guard however as those are the most conventional (thus familiar) approaches used by Swift programmers.但是,您可能应该使用ifguard ,因为这些是 Swift 程序员使用的最传统(因此很熟悉)的方法。

Optional可选的

Also you can use Nil-Coalescing Operator您也可以使用Nil-Coalescing Operator

The nil-coalescing operator ( a ?? b ) unwraps an optional a if it contains a value, or returns a default value b if a is nil . nil-coalescing operator ( a ?? b ) 如果可选a包含a值,则解包它,如果anil a则返回默认值b The expression a is always of an optional type.表达式 a 始终是可选类型。 The expression b must match the type that is stored inside a .表达式b必须匹配存储在a中的类型。

let value = optionalValue ?? defaultValue

If optionalValue is nil , it automatically assigns value to defaultValue如果optionalValuenil ,它会自动将 value 分配给defaultValue

Now you can do in swift the following thing which allows you to regain a little bit of the objective-c if nil else现在您可以快速执行以下操作, if nil else ,您可以重新获得一点目标-c

if textfieldDate.text?.isEmpty ?? true {

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

This test worked as expected in all cases.该测试在所有情况下都按预期工作。 BTW, you do not need the brackets () .顺便说一句,您不需要括号()

If you have conditional and would like to unwrap and compare, how about taking advantage of the short-circuit evaluation of compound boolean expression as in如果您有条件并且想要展开和比较,那么如何利用复合布尔表达式的短路评估,如

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

}

Granted, this is not as readable as some of the other suggested posts, but gets the job done and somewhat succinct than the other suggested solutions.当然,这不像其他一些建议的帖子那样可读,但可以完成工作并且比其他建议的解决方案更简洁。

If someone is also try to find to work with dictionaries and try to work with Optional(nil).如果有人也尝试使用字典并尝试使用 Optional(nil)。

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

You will end up with the type Double??.你最终会得到 Double?? 类型。

To continue on your code, just use coalescing to get around it.要继续处理您的代码,只需使用合并来绕过它。

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

Now you just have Double?现在你只有双?

This is totally logical, but I searched the wrong thing, maybe it helps someone else.这完全合乎逻辑,但我搜索了错误的东西,也许它可以帮助别人。

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

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