检查可选布尔值


88

当我想检查Optional Bool是否为true时,这样做不起作用:

var boolean : Bool? = false
if boolean{
}

导致此错误:

可选类型“ @IvalueBool?” 不能用作布尔值;测试'!= nil'代替

我不想检查零。我想检查返回的值是否为true。

if boolean == true如果我正在与可选布尔一起工作,是否总是必须做?

由于OptionalsBooleanType不再兼容,编译器难道不应该知道我要检查Bool的值吗?


由于布尔值符合Equatable协议,因此您可以将可选与非可选进行比较。看到这里
亲爱的

Answers:


192

使用可选的布尔值时,需要使检查明确:

if boolean == true {
    ...
}

否则,您可以打开可选的包装:

if boolean! {
    ...
}

但是,如果boolean为nil- ,则会生成运行时异常,以防止发生这种情况:

if boolean != nil && boolean! {
    ...
}

在Beta 5之前可以实现,但已根据发行说明中的​​内容进行了更改:

当它们具有一个值时,它们不再隐式地为true;没有它们时则不再为false,以避免在使用可选的Bool值时产生混淆。而是使用==或!=运算符对nil进行显式检查,以找出可选值是否包含值。

附录:@MartinR建议,对第三个选项的更紧凑的变化是使用合并运算符:

if boolean ?? false {
    // this code runs only if boolean == true
}

这意味着:如果布尔值不为nil,则表达式的计算结果为布尔值(即,使用展开的布尔值),否则,表达式的计算结果为 false


4
第三个选项是首选解决方案,因为它是表达代码意图的最佳方法。使用if let也可以。
苏珊(Sulthan)2014年

29
第三个选项的一个变种,使用“空合并运算符??”: if boolean ?? false { ... }
Martin R

4
但是,如果我想否定它,那就开始看起来很荒谬:if !(boolean ?? true) { ... }:(
Andreas

2
强迫拆开一个最大的坏主意。应始终避免。
Matthieu Riegler

4
第一种选择怎么了?在我看来,这似乎是最好的方法。
Vahid Amiri '18

43

可选绑定

斯威夫特3&4

var booleanValue : Bool? = false
if let booleanValue = booleanValue, booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

斯威夫特2.2

var booleanValue : Bool? = false
if let booleanValue = booleanValue where booleanValue {
    // Executes when booleanValue is not nil and true
    // A new constant "booleanValue: Bool" is defined and set
    print("bound booleanValue: '\(booleanValue)'")
}

如果是,则代码let booleanValue = booleanValue返回false,并且该块不执行。如果不是,则此代码定义一个名为type的新变量(而不是可选的)。booleanValuenilifbooleanValuenilbooleanValueBoolBool?

Swift 3和4代码booleanValue(以及Swift 2.2代码where booleanValue)将评估新booleanValue: Bool变量。如果为true,则该if块将使用booleanValue: Bool范围内新定义的变量执行(允许选择在该if块内再次引用绑定值)。

注意:将绑定的常量/变量命名为可选常量/变量(例如)是一种Swift约定let booleanValue = booleanValue。这种技术称为可变阴影。您可以打破常规并使用let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue。我指出这一点是为了帮助您了解正在发生的事情。我建议使用可变阴影。

 

其他方法

无合并

对于这种特定情况,零合并是显而易见的

var booleanValue : Bool? = false
if booleanValue ?? false {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

检查false不清楚

var booleanValue : Bool? = false
if !(booleanValue ?? false) {
    // executes when booleanValue is false
    print("optional booleanValue: '\(booleanValue)'")
}

注意:if !booleanValue ?? false不编译。

 

强制展开可选(避免)

强制展开会增加某些人在将来进行更改但编译时崩溃但在运行时崩溃的机会。因此,我会避免这样的事情:

var booleanValue : Bool? = false
if booleanValue != nil && booleanValue! {
    // executes when booleanValue is true
    print("optional booleanValue: '\(booleanValue)'")
}

 

通用方法

尽管此堆栈溢出问题专门询问如何检查aBool?是否trueif语句内,但有助于确定一种通用方法,即检查true,false还是将未包装的值与其他表达式组合。

随着表达式变得越来越复杂,我发现可选绑定方法比其他方法更灵活,更易于理解。注意与任何可选类型(即可选结合的作品Int?String?等等)。


我在使用布尔表达式和可选的while循环时遇到困难。nil合并运算符可以工作,但是它很凌乱且容易出错。有使用的方法if let吗?
jbaraga

@jbaraga,请发布您想知道的while循环示例。
Mobile Dan

在将数组用作堆栈时,我想弹出值直到满足条件或堆栈为空。例如,while array.last < threshold { array.removeLast() }
jbaraga

您可以实现这一目标栈处理与if, let, where利用这样的:while let last = array.last where last < threshold { array.removeLast() }在斯威夫特2或while let last = array.last, last < threshold { array.removeLast() }斯威夫特3
移动丹

更好,谢谢。我不知道while let
jbaraga

1
var enabled: Bool? = true

if let enabled = enabled, enabled == true {
    print("when is defined and true at the same moment")
}

if enabled ?? false {
    print("when is defined and true at the same moment")
}

if enabled == .some(true) {
    print("when is defined and true at the same moment")
}

if enabled == (true) {
    print("when is defined and true at the same moment")
}

if case .some(true) = enabled {
    print("when is defined and true at the same moment")
}

if enabled == .some(false) {
    print("when is defined and false at the same moment")
}

if enabled == (false) {
    print("when is defined and false at the same moment")
}

if enabled == .none {
    print("when is not defined")
}

if enabled == nil {
    print("when is not defined")
}

0

我找到了另一个解决方案,使布尔运算符重载。例如:

public func < <T: Comparable> (left: T?, right: T) -> Bool {
    if let left = left {
        return left < right
    }
    return false
}

这可能不完全是语言更改的“精神”,但是它允许安全展开可选内容,并且可用于任何条件的条件,包括while循环。


1
抱歉,回头看原始帖子,它没有回答该特定问题,而是我在之前的评论中提出的问题。
jbaraga

对于使用此重载,我会非常小心,因为在某些情况下,您可能不希望将nil视为“大于”非nil值(在某些情况下,您可能希望获得相反的结果,或者可能完全处理)。相反,使用常规展开方法会强制您明确解决每种情况下要处理nil的方式,因此不太可能遇到意外结果。
约翰·蒙哥马利

0

我发现最容易理解的答案是定义一个函数。不是很复杂,但是可以完成工作。

func isTrue(_ bool: Bool?) -> Bool {
    guard let b = bool else {
        return false
    }
    return b
}

用法:

let b: Bool? = true
if isTrue(b) {
    // b exists and is true
} else {
    // b does either not exist or is false
}

0

正如安东尼奥所说

当它们具有一个值时,它们不再隐式地为true;没有它们时则不再为false,以避免在使用可选的Bool值时产生混淆。而是使用==或!=运算符对nil进行显式检查,以找出可选值是否包含值。

我花了几个小时试图理解我偶然发现的一行代码,但是这个线程使我走上了正确的道路。

这句话是从2014年8月,从那时起,苹果公司推出Never以下建议SE-0102和后者使其符合Equatable,哈希的,错误和可比

现在可以检查布尔值是否在nil使用Never?


var boolean: Bool? = false
boolean is Never? // false
boolean = true
boolean is Never? // false
boolean = nil
boolean is Never? // true

您实际上可以使用其他任何不适合居住的类型:

public enum NeverEver { }
var boolean: Bool? = false
boolean is NeverEver? // false
boolean = true
boolean is NeverEver? // false
boolean = nil
boolean is NeverEver? // true

话虽如此,现在也可以使用属性包装器

@propertyWrapper struct OptionalBool {
    public var wrappedValue: Bool?
    public var projectedValue: Bool { wrappedValue ?? false }
    public init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate {
            return "predicate is true"
        }
        return "predicate is false"
    }
}

var object = Struct()
object.description // "predicate is false"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"

甚至:

@propertyWrapper struct OptionalBool {
    var wrappedValue: Bool?
    var projectedValue: OptionalBool { self }
    var isNil: Bool { wrappedValue is Never? }
    var value: Bool { wrappedValue ?? false }
    
    init(wrappedValue: Bool?) {
        self.wrappedValue = wrappedValue
    }
}

struct Struct {
    @OptionalBool var predicate: Bool?
    var description: String {
        if $predicate.value {
            return "predicate is true"
        }
        if !$predicate.isNil {
            return "predicate is false"
        }
        return "predicate is nil"
    }
}

var object = Struct()
object.description // "predicate is nil"
object.predicate = false
object.description // "predicate is false"
object.predicate = true
object.description // "predicate is true"

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.