Перевірка значення необов’язкового Bool


88

Коли я хочу перевірити, чи необов’язковий Bool відповідає дійсності, це не працює:

var boolean : Bool? = false
if boolean{
}

Це призводить до цієї помилки:

Додатковий тип '@IvalueBool?' не може використовуватися як логічна; замість цього перевірити на '! = nil'

Я не хочу перевіряти нуль; Я хочу перевірити, чи повертається значення відповідає дійсності.

Чи завжди мені доводиться робити, if boolean == trueякщо я працюю з необов’язковим Bool?

Оскільки Optionals більше не відповідають BooleanType, чи не повинен компілятор знати, що я хочу перевірити значення Bool?


Оскільки логічні значення відповідають протоколу Equatable, тоді ви можете порівняти необов’язковий із необов’язковим. Дивіться тут
Мед

Відповіді:


192

З необов’язковими булевими значеннями потрібно зробити перевірку явною:

if boolean == true {
    ...
}

В іншому випадку ви можете розгорнути необов’язковий:

if boolean! {
    ...
}

Але це генерує виняток виконання, якщо логічне значення nil- щоб запобігти цьому:

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

До бета-версії 5 це було можливо, але воно було змінено, як повідомляється в примітках до випуску:

Необов’язкові більше не імпліцитно оцінюють як true, коли вони мають значення, і false, коли їх немає, щоб уникнути плутанини при роботі з необов’язковими значеннями Bool. Натомість зробіть явну перевірку проти nil за допомогою операторів == або! =, Щоб з’ясувати, чи містить необов’язкове значення.

Додаток: як запропонував @MartinR, більш компактним варіантом третього варіанту є використання оператора коалесценції:

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

що означає: якщо логічне значення не дорівнює нулю, вираз обчислюється булевим значенням (тобто за допомогою розгорнутого логічного значення), інакше вираз обчислюється як false


4
Третій варіант є кращим рішенням, оскільки це найкращий спосіб виразити намір коду. Використання if letтакож буде працювати.
Султан

29
Варіант третього варіанту, використовуючи «нульовий оператор коалесцирует ??»: if boolean ?? false { ... } .
Martin R

4
Але якщо я хочу заперечити це, це починає виглядати безглуздо: if !(boolean ?? true) { ... }:(
Андреас,

2
Примусово розгортати вкрай погану ідею. Цього завжди слід уникати.
Matthieu Riegler

4
Що поганого в першому варіанті? Мені це здається найкращим способом.
Вахід Амірі,

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якщо booleanValueє, nilа ifблок не виконується. Якщо booleanValueні nil, цей код визначає нову змінну з іменем booleanValueтипу Bool(замість необов’язкової, Bool?).

Код Swift 3 і 4 booleanValue(і код Swift 2.2 where booleanValue) оцінює нову booleanValue: Boolзмінну. Якщо це істина, 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)'")
}

 

Загальний підхід

Незважаючи на те, що це запитання про переповнення стека конкретно задає питання, як перевірити, чи a Bool?знаходиться trueв ifоператорі, корисно визначити загальний підхід - перевірка на істинність, хибність чи поєднання розгорнутого значення з іншими виразами.

Оскільки вираз ускладнюється, я вважаю необов’язковий підхід до прив’язки більш гнучким та легшим для розуміння, ніж інші підходи. Зверніть увагу , що додаткові обов'язкові роботи з будь-яким додатковим типом ( Int?, String?і т.д.).


У мене виникають труднощі з використанням булевих виразів з необов’язковими циклами for while. Нульовий оператор злиття працює, але він брудний і схильний до помилок. Чи є спосіб використовувати 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() }в Swift 2 або while let last = array.last, last < threshold { array.removeLast() }в Swift 3.
Mobile Dan

Так краще, дякую. Я цього не знав 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 розглядалося як "більше ніж" значення, яке не дорівнює нулю (можливо, ви хочете отримати протилежний результат у певних контекстах, або, можливо, альтернативно поводження повністю). Натомість використання звичайного розгортання змушує вас чітко вказати, як ви хочете обробляти нулі в кожному випадку, тому ви з меншою ймовірністю отримаєте несподівані результати.
Джон Монтгомері,

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 року , і з тих пір Apple представила Neverнаступну пропозицію SE-0102, а остання зробила її відповідною стандартам Equatable, Hashable, Error і Comparable

Тепер можна перевірити , якщо булева це з 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"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.