Comprobación del valor de un bool opcional

88

Cuando quiero verificar si un bool opcional es verdadero, hacer esto no funciona:

var boolean : Bool? = false
if boolean{
}

Resulta en este error:

Tipo opcional '@IvalueBool?' no se puede utilizar como booleano; prueba para '! = nil' en su lugar

No quiero comprobar nada; Quiero comprobar si el valor devuelto es verdadero.

¿Tengo que hacerlo siempre if boolean == truesi estoy trabajando con un bool opcional?

Dado que los opcionales ya no se ajustan BooleanType, ¿no debería saber el compilador que quiero verificar el valor de Bool?

Gato luna
fuente
Dado que los booleanos se ajustan al protocolo Equatable, puede comparar un opcional con uno no opcional. Ver aquí
Miel

Respuestas:

192

Con booleanos opcionales, es necesario hacer la verificación explícita:

if boolean == true {
    ...
}

De lo contrario, puede desenvolver el opcional:

if boolean! {
    ...
}

Pero eso genera una excepción de tiempo de ejecución si boolean es nil, para evitar eso:

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

Antes de la beta 5 era posible, pero se ha cambiado como se informa en las notas de la versión:

Los opcionales ya no evalúan implícitamente como verdadero cuando tienen un valor y falso cuando no lo tienen, para evitar confusiones cuando se trabaja con valores bool opcionales. En su lugar, haga una comprobación explícita contra nil con los operadores == o! = Para averiguar si un opcional contiene un valor.

Anexo: como lo sugiere @MartinR, una variación más compacta de la tercera opción es usar el operador coalescente:

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

lo que significa: si booleano no es nil, la expresión se evalúa como el valor booleano (es decir, utilizando el valor booleano no envuelto); de lo contrario, la expresión se evalúa como false

Antonio
fuente
4
La tercera opción es la solución preferida porque es la mejor manera de expresar la intención del código. Usar if lettambién funcionaría.
Sulthan
29
Una variante de la tercera opción, usando el "operador de coalescencia nil ??": if boolean ?? false { ... } .
Martin R
4
Pero si quiero negarlo, comienza a verse ridículo: if !(boolean ?? true) { ... }:(
Andreas
2
Obligado a desenvolver una muy mala idea. Siempre debe evitarse.
Matthieu Riegler
4
¿Qué pasa con la primera opción? A mí me parece la mejor forma.
Vahid Amiri
43

Enlace opcional

Swift 3 y 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)'")
}

Swift 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)'")
}

El código let booleanValue = booleanValuedevuelve falsesi booleanValuees nily el ifbloque no se ejecuta. Si booleanValueno es así nil, este código define una nueva variable denominada booleanValuede tipo Bool(en lugar de opcional Bool?).

El código Swift 3 y 4 booleanValue(y el código Swift 2.2 where booleanValue) evalúa la nueva booleanValue: Boolvariable. Si es cierto, el ifbloque se ejecuta con la booleanValue: Boolvariable recién definida en el alcance (lo que permite que la opción haga referencia al valor enlazado nuevamente dentro del ifbloque).

Nota: Es una convención de Swift nombrar la constante / variable ligada de la misma manera que la constante / variable opcional como let booleanValue = booleanValue. Esta técnica se denomina sombreado variable . Podrías romper con las convenciones y usar algo como let unwrappedBooleanValue = booleanValue, unwrappedBooleanValue. Señalo esto para ayudar a comprender lo que está sucediendo. Recomiendo usar sombreado variable.

 

Otros enfoques

Nil coalescencia

La fusión nula es clara para este caso específico

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

La comprobación falseno es tan clara

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

Nota: if !booleanValue ?? falseno se compila.

 

Forzar desenvolver opcional (evitar)

Forzar el desenvolvimiento aumenta la posibilidad de que alguien haga un cambio en el futuro que compila pero falla en tiempo de ejecución. Por lo tanto, evitaría algo como esto:

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

 

Un enfoque general

Aunque esta pregunta de desbordamiento de pila pregunta específicamente cómo verificar si a Bool?está truedentro de una ifdeclaración, es útil identificar un enfoque general, ya sea verificar verdadero, falso o combinar el valor sin envolver con otras expresiones.

A medida que la expresión se vuelve más complicada, encuentro que el enfoque de enlace opcional es más flexible y más fácil de entender que otros enfoques. Tenga en cuenta que las obras de enlace opcionales con cualquier tipo opcional ( Int?, String?, etc.).

Dan móvil
fuente
Tengo dificultades para usar expresiones booleanas con bucles for while opcionales. El operador de fusión nula funciona, pero es complicado y propenso a errores. ¿Hay alguna forma de utilizarlo if let?
jbaraga
@jbaraga, publique un ejemplo del bucle while sobre el que se está preguntando.
Dan móvil
Al usar una matriz como una pila, quiero sacar valores hasta que se cumpla una condición o la pila esté vacía. Por ejemplo,while array.last < threshold { array.removeLast() }
jbaraga
Puede lograr ese procesamiento de pila if, let, whereusando esto: while let last = array.last where last < threshold { array.removeLast() }en Swift 2 o while let last = array.last, last < threshold { array.removeLast() }en Swift 3.
Mobile Dan
Eso está mejor, gracias. Yo no estaba al tanto 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")
}
Blazej SLEBODA
fuente
0

Encontré otra solución, sobrecargando los operadores booleanos. Por ejemplo:

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

Esto puede no estar totalmente en el "espíritu" de los cambios de lenguaje, pero permite desenvolver de forma segura los opcionales y se puede utilizar para los condicionales en cualquier lugar, incluidos los bucles while.

jbaraga
fuente
1
Lo siento, mirando hacia atrás en la publicación original, no responde a esa pregunta específica, sino a la pregunta que planteé en mi comentario anterior.
jbaraga
Sería muy cuidadoso al usar esta sobrecarga, porque puede haber casos en los que no desee que nil sea tratado como "mayor que" un valor distinto de nil (es posible que desee el resultado opuesto en ciertos contextos, o posiblemente una alternativa manejo en su totalidad). En su lugar, utilizar el desenvolvimiento normal lo obliga a abordar explícitamente cómo desea manejar los nulos en cada caso, por lo que es menos probable que se encuentre con resultados inesperados.
John Montgomery
0

La respuesta que encontré más fácil de leer es definir una función. No es muy complicado, pero funciona.

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

uso:

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

Como dijo Antonio

Los opcionales ya no evalúan implícitamente como verdadero cuando tienen un valor y falso cuando no lo tienen, para evitar confusiones cuando se trabaja con valores bool opcionales. En su lugar, haga una comprobación explícita contra nil con los operadores == o! = Para averiguar si un opcional contiene un valor.

Pasé algunas horas tratando de entender una línea de código con la que me topé, pero este hilo me puso en el camino correcto.

Esta cita es de agosto de 2014 y, desde entonces, Apple presentó la Neversiguiente propuesta SE-0102 y la hizo cumplir con Equatable, Hashable, Error y Comparable.

Ahora es posible verificar si un booleano está nilusando Never?:


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

De hecho, puede usar cualquier otro tipo inhabitable :

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

Dicho esto, también es posible usar un contenedor de propiedades ahora:

@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"

o incluso:

@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"

AnderCover
fuente