Странное использование необязательных значений в Swift

    var z1 = false
    var z2 = false
    var z3 = false


    if let y1: Int = nil {
        z1 = true
    }

    if let y2: Int? = nil {
        z2 = true
    }

    var x: Int? = nil
    if let y3: Int? = x {
        z3 = true
    }

    println(z1)   // false
    println(z2)   // ture
    println(z3)   // false

Я пытаюсь использовать необязательное значение, подобное этому (я знаю, что это утомительно, мне просто любопытно узнать о необязательном значении в глубине).

Результат не такой, как я ожидаю. z1 ложно, этого я и ожидал, но почему z2 верно? y2 равен нулю после присваивания, но оператор if считает, что это выражение (let y2: Int? = nil) истинно, и выполняется следующий оператор (z2=true), почему это произошло?

Почему z2 не может быть ложным?

Если y2 просто объявлено как необязательное значение, которое может содержать другое необязательное значение, а необязательное значение может быть нулевым, оператор if let просто определяет, что развертывание выполнено успешно, то почему z3 не соответствует действительности?


person 6david9    schedule 05.06.2014    source источник


Ответы (5)


Это потому, что вы можете объявить необязательную переменную рекурсивно необязательной, например:

var x: Int?? = 5
// LLDB: p x.dynamicType returns (Int??)

Вы можете пойти еще дальше:

var y: Int????????? = 6
// LLDB: p x.dynamicType returns (Int?????????)

Что делает конструкция if let, так это проверяет, можете ли вы развернуть необязательное значение, и бывает, что вы можете развернуть необязательное необязательное значение, даже если его окончательное значение равно nil. Думайте об этом так, как будто необязательное значение — это не nil, а некий объект, обертывающий nil.

Чтобы проверить это в коде, вы можете сделать следующее:

var a: Int? = nil;
// LLDB: p a! returns fatal error: Can't unwrap Optional.None

var b: Int?? = nil;
// LLDB: p b! returns (Int?) $R2 = nil

Итак, вот что произошло в случае вашего условия z2: конструкция if let проверяет, может ли она развернуть y2, и это было так, поэтому блок if был выполнен.

person MDJ    schedule 05.06.2014
comment
привет, MDJ, я обновил тестовый пример, добавив y3. Как вы упомянули выше, y2 является необязательным значением, которое может содержать другое значение optionanl, оператор if let просто проверяет успешность развертывания. Но почему утверждение y3 if let ложно? x — необязательное значение, y3 — контейнер необязательного значения, почему присваивание не выполняется? - person 6david9; 05.06.2014

Необязательная привязка работает следующим образом: if проверяет успешность объявления let. В вашем первом операторе это не удается, потому что вы не можете присвоить nil Int, но во втором операторе он успешен и входит в тело, потому что вам разрешено присваивать nil IntOptional.

person Connor    schedule 05.06.2014

Для меня z2 ложно, а z3 верно:

println(z1)   // false
println(z2)   // false
println(z3)   // true

Напоминая, что if let A = optB { ... } означает, по сути, «если optB не является nil, назначьте его A и выполните следующий блок», причина, по которой z3 истинно, заключается в том, что x является Optional(nil), а не nil. Вы можете увидеть это, попробовав

var x: Int? = 5
if let y3: Int? = x {
    println(x) // Optional(5)
    z3 = true
}
println(z3)    // true

и сравнивая эту и оригинальную версию с

var x: Int? = nil
if let y3: Int? = x? {
    z3 = true
}
println(z3)   // false!

В первом случае вы можете видеть, что x завернуто (поэтому исходная версия была не nil, а Optional(nil)); во втором вы можете увидеть, как x можно развернуть, чтобы получить nil.

Swift (особенно при просмотре на игровой площадке Xcode) затрудняет просмотр этого из-за того, как он отображает nil (вероятно, потому, что его нельзя развернуть, он показывает Optional(nil) и nil как nil).


Кроме того, вариации этого можно также использовать для изучения некоторых тонкостей того, что происходит в левой части присваивания, что является единственной разницей между случаями z1 и z2. Например, вы можете видеть, что в случае z3, как и в случае z2, y3 является необязательным.

var x: Int? = 5
if let y3: Int? = x {
    println(x) // Optional(5)
    println(y3) // Optional(5)
    z3 = true
}
println(z3)   // true

в то время как удаление ? из типа для y3 делает его похожим на случай z1, где y1 не является необязательным

var x: Int? = 5
if let y3: Int = x {
    println(x) // Optional(5)
    println(y3) // 5
    z3 = true
}
println(z3)   // true
person orome    schedule 26.10.2014

Потому что вам не хватает "?" в z1

person Chéyo    schedule 05.06.2014
comment
Сравните два утверждения «если», разница между ними заключается в том, что? после Типа, последний из которых имеет '?' после Типа, но они дают два разных значения. Почему это произошло? Это какая-то языковая особенность или просто дефект Свифта? - person 6david9; 05.06.2014

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

var z1 = false  
var z2 = false  
var z3 = false  


if let y1: Int = nil /* nil : Optional<T>   instantiated to Optional<Int>, value is .None */ 
{
    z1 = true
}

if let y2: Int? = nil /* nil : Optional<T>   instantiated to Optional<Optional<Int>>, value is .Some(.None) */ 
{
    z2 = true
}

var x: Int? = nil   /* nil : Optional<T>   instantiated to Optional<Int>, value is .None */ 
if let y3: Int? = x /* x : Optional<Int>   value is .None */ 
{
    z3 = true
}

println(z1)   // false
println(z2)   // ture
println(z3)   // false
person Analog File    schedule 07.06.2014