Что не так со сравнением необязательных bool в одной структуре if в swift

Я некоторое время программировал на Swift, и я думаю, что должен был поставить ! на все мои переменные поля let, которые не были определены сразу.

Сегодня я замечаю, что этот кусок кода не компилируется, и я очень удивлен? почему это?

class MyClass : Mapper {
    var a: Bool!

    required init?(_ map: Map) {
    }

    // Mappable
    func mapping(map: Map) {
        a   <- map["a"]
    }
}

let myClass = MyClass()

if myClass.a { // Compiler not happy
    //  Optional type 'Bool!' cannot be used as a boolean; test for '!= nil' instead
}

if true && myClass.a { // Compiler happy

}

if myClass.a && myClass.a { // Compiler happy

}

Apple Swift версии 2.2

Изменить
Некоторые люди указывают, почему я использую let для переменной, которая никогда не изменится. Я упомянул, что это для переменных поля, но я сокращаю пример. При использовании ObjectMapper (http://github.com/Hearst-DD/ObjectMapper) все поля не определяются сразу в файле init. Вот почему они все либо необязательны? или обязательно!


person jsgoupil    schedule 06.05.2016    source источник
comment
В операторах if и switch неявно развернутые опции не разворачиваются автоматически принудительно (если вы просто используете их сами по себе). Это чтобы можно было смело сравнивать их с nil. См. stackoverflow.com/questions/33670991/   -  person Hamish    schedule 06.05.2016
comment
Кратчайший обходной путь: if a! {   -  person vacawama    schedule 06.05.2016
comment
Вау, это очень непоследовательно, и это становится пугающим для рецензентов кода. Они спросят, почему я не использую оператор if let для предотвращения сбоя. Если я использую if true && a, меня уволят.   -  person jsgoupil    schedule 06.05.2016
comment
Для безопасности if a ?? false {, что менее странно, чем if let a = a where a {.   -  person vacawama    schedule 06.05.2016
comment
... а также менее уродливый, чем if case true? = a { :)   -  person Martin R    schedule 06.05.2016
comment
Свойство let почти никогда не должно быть неявно развернутым необязательным. Измените его на обычный Bool.   -  person rob mayoff    schedule 06.05.2016
comment
@dfri, if a.boolValue { вылетит, если a будет nil. Как и if a as Bool {.   -  person vacawama    schedule 06.05.2016
comment
@originaluser2 Основная причина — использование ObjectMapper github.com/Hearst-DD/ObjectMapper. . Я должен установить некоторые свойства как? и прочее как !.   -  person jsgoupil    schedule 06.05.2016
comment
@vacawama Действительно; это также верно для вашего собственного кратчайшего обходного пути if a! { (безопасность этих обходных путей - это еще одно обсуждение, imo; как пишет Роб Мэйофф; зачем вообще использовать неявный развернутый необязательный для этого случая)   -  person dfrib    schedule 06.05.2016
comment
@dfri, я понял, что ваше многоточие означает, что оно помечает комментарии к безопасным предложениям.   -  person vacawama    schedule 06.05.2016
comment
@vacawama Ах, я пропустил, что многоточие естественным образом связывается с вашим В целях безопасности..., я слишком быстро просмотрел предыдущие комментарии и просто подумал, что мы перечисляем альтернативы, чтобы избежать ошибки времени компиляции, мой плохой!   -  person dfrib    schedule 06.05.2016
comment
Возможно, вам следует обновить свой вопрос конкретным вариантом использования. Не может ли a быть nil, если объект инициализируется из какого-то JSON?   -  person Martin R    schedule 06.05.2016
comment
@dfri, без проблем. Это была веселая дискуссия со всех сторон.   -  person vacawama    schedule 06.05.2016
comment
@MartinR Здесь я обновил вариант использования. Теперь вопрос полностью теряется с использованием сторонней библиотеки, где я задавал простой вопрос Swift, почему Swift должен быть таким строгим в отношении структуры if.   -  person jsgoupil    schedule 06.05.2016


Ответы (2)


Немного истории...

В Swift 1.0 можно было проверить, содержит ли необязательная переменная optVar значение, просто проверив:

if optVar {
    println("optVar has a value")
} else {
    println("optVar is nil")
}

В обновлении Язык программирования Swift для Swift 1.1 (от 16 октября 2014 г.) указано:

Необязательные параметры больше не неявно оцениваются как true, когда они имеют значение, и false, когда они не имеют значения, чтобы избежать путаницы при работе с необязательными значениями Bool. Вместо этого выполните явную проверку nil с помощью операторов == или !=, чтобы узнать, содержит ли необязательный параметр значение.

Итак, бессмысленное сообщение об ошибке, которое вы получаете, было помещено туда, потому что компилятор Swift интерпретирует ваш:

if a {
}

значить:

if a != nil {
}

и это побуждает вас протестировать против nil, чтобы определить, имеет ли значение Необязательный a.

Возможно, авторы Swift изменят его в будущем, но пока вам придется явно разворачивать a:

if a! {
}

или проверить против true:

if a == true {
}

или (для полной безопасности):

if a ?? false {
    print("this will not crash if a is nil")
}
person vacawama    schedule 06.05.2016

Вы можете объявить let a: Bool без ! и без объявления истинного или ложного права. Компилятор будет жаловаться, если он не может гарантировать, что значение будет установлено до того, как вы его используете.

Это работает.

let a: Bool

a = true

if a { // Compiler happy

}

Это тоже работает, потому что a гарантированно будет установлено.

let a: Bool

if thingOne < thingTwo {
    a = true
} else {
    a = false
}

if a { // Compiler happy

}

Однако это не сработает, потому что a не гарантируется до того, как вы попытаетесь его использовать.

let a: Bool

if thingOne < thingTwo {
    a = true
}

if a { // Compiler NOT happy
    // "Constant 'a' used before being initialized"
}

Теперь, если вы не можете гарантировать, что ваша переменная будет установлена ​​к тому времени, когда вы выполните проверку, вам действительно следует использовать необязательный var в первую очередь.

var a: Bool?

if a == true { // Compiler happy

}
person Mike Cole    schedule 06.05.2016
comment
Я ценю ответ. Основная причина, по которой я не мог избавиться от своего ! потому что я использую ObjectMapper github.com/Hearst-DD/ObjectMapper, и это рекомендуется использовать ! в этой ситуации. - person jsgoupil; 06.05.2016
comment
Без дополнительного контекста трудно сказать, подходит ли использование ! в вашем случае. Определенно есть некоторые странные ситуации, в частности, с сериализацией JSON. NSJSONSerialization, например, не может обрабатывать дополнительные параметры и вместо этого требует объект NSNull. В зависимости от того, что вы там делаете и как работает ObjectMapper, это может понадобиться. - person Mike Cole; 06.05.2016