Что такое "развернутое значение" в Swift?

Я изучаю Swift для iOS 8/OSX 10.10, следуя этот учебник, а термин " развернутое значение" используется несколько раз, как в этом пункте (под объектами и классом):

При работе с дополнительными значениями вы можете написать? перед операциями как методы, свойства и подписи. Если значение перед? нуль, все после? игнорируется, а значение целого выражение равно нулю. В противном случае необязательное значение будет развернуто, и все после? действует на развернутое значение. В обоих случаях значение всего выражения является необязательным значением.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare?.sideLength

Я не понимаю его и искал в Интернете без везения.

Что это значит?


Изменить

От Cezary ответьте, есть небольшая разница между выходом исходного кода и окончательным решением (тестируется на игровой площадке):

Исходный код

Original code

Решение Цезари

Cezary's solution

Свойства суперкласса отображаются на выходе во втором случае, тогда как в первом случае есть пустой объект.

Разве результат не должен быть одинаковым в обоих случаях?

Связанный Q & A: Что такое необязательное значение в Swift?

Ответ 1

Во-первых, вы должны понять, что такое необязательный тип. Необязательный тип в основном означает, что переменная может быть nil.

Пример:

var canBeNil : Int? = 4
canBeNil = nil

Значок вопроса указывает на то, что canBeNil может быть nil.

Это не сработает:

var cantBeNil : Int = 4
cantBeNil = nil // can't do this

Чтобы получить значение из вашей переменной, если оно является необязательным, вы должны развернуть его. Это просто означает, что в конце есть восклицательный знак.

var canBeNil : Int? = 4
println(canBeNil!)

Ваш код должен выглядеть так:

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare!.sideLength

Поощрение:

Вы также можете объявить необязательные опции для автоматического разворота с помощью восклицательного знака вместо вопросительного знака.

Пример:

var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed

Итак, альтернативный способ исправить ваш код:

let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare.sideLength

EDIT:

Разница, которую вы видите, является именно тем признаком, что необязательное значение завершено. На нем есть еще один слой. развернутая версия просто показывает прямой объект, потому что он, ну, разворачивается.

Сравнение быстрой игровой площадки:

playground

В первом и втором случаях объект не разворачивается автоматически, поэтому вы видите два "слоя" ({{...}}), тогда как в третьем случае вы видите только один слой ({...}), потому что объект автоматически распаковывается.

Разница между первым случаем и вторыми двумя случаями заключается в том, что во втором случае вам будет предоставлена ​​ошибка времени выполнения, если optionalSquare установлено на nil. Используя синтаксис в первом случае, вы можете сделать что-то вроде этого:

if let sideLength = optionalSquare?.sideLength {
    println("sideLength is not nil")
} else {
    println("sidelength is nil")
}

Ответ 2

Существующий правильный ответ велик, но я обнаружил, что для того, чтобы понять это полностью, мне нужна была хорошая аналогия, поскольку это очень абстрактная и странная концепция.

Итак, позвольте мне помочь тем разработчикам "правых мозгов" (визуальное мышление), предоставив другую точку зрения в дополнение к правильному ответу. Вот хорошая аналогия, которая мне очень помогла.

Аналогия с анатомированием на день рождения

Думайте о вариантах как о подарках на день рождения, которые приходят в жесткой, жесткой, цветной упаковке.

Вы не знаете, есть ли что-нибудь внутри упаковки, пока вы не разворачиваете настоящее - возможно, внутри ничего нет! Если внутри есть что-то, это может быть еще один подарок, который также завернут, и который также может содержать ничего. Вы могли бы даже развернуть 100 вложенных подарков, чтобы наконец обнаружить, что нет ничего, кроме обертывания.

Если значение необязательного значения не равно nil, теперь вы обнаружили поле, содержащее что-то. Но, особенно если значение не указано явно и является переменной, а не предопределенной константой, вам все равно может понадобиться открыть окно, прежде чем вы сможете узнать что-то конкретное о том, что в поле, например, о том, что это такое, или о том, что фактическое значение.

Что в коробке?! Аналогия

Даже после того, как вы разворачиваете переменную, вы по-прежнему похожи на Брэда Питта в последней сцене в SE7EN (предупреждение: спойлеры и очень R-rated грязный язык и насилие), потому что даже после того, как вы развернули настоящее, вы находитесь в следующей ситуации: у вас теперь есть nil или поле, содержащее что-то (но вы не знаете, что).

Возможно, вы знаете тип чего-то. Например, если вы объявили переменную как тип, [Int:Any?], то вы бы знали, что у вас есть (потенциально пустой) словарь с целыми индексами, которые дают завернутое содержимое любого старого типа.

Вот почему обращение к типам коллекций (словари и массивы) в Swift может выглядеть как волосатое.

Пример:

typealias presentType = [Int:Any?]

func wrap(i:Int, gift:Any?) -> presentType? {
    if(i != 0) {
        let box : presentType = [i:wrap(i-1,gift:gift)]
        return box
    }
    else {
        let box = [i:gift]
        return box
    }
}

func getGift() -> String? {
    return "foobar"
}

let f00 = wrap(10,gift:getGift())

//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.

var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])

//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is

let asdf : String = b4r!! as! String

print(asdf)

Ответ 3

Свифт помещает высокую премию на безопасность типа. Весь язык Swift был разработан с учетом безопасности. Это одна из отличительных черт Свифта, которую вы должны приветствовать с распростертыми объятиями. Это поможет в разработке чистого, читаемого кода и поможет предотвратить сбой приложения.

Все опции в Swift разделены символом ?. Установив ? после имени типа, в котором вы объявляете как необязательный, вы по существу отливаете это не как тип, который находится перед ?, а вместо этого как необязательный тип.


Примечание. Переменная или тип Int не совпадает с Int?. Это два разных типа, которые не могут работать друг с другом.


Использование необязательного

var myString: String?

myString = "foobar"

Это означает, что не означает, что вы работаете с типом String. Это означает, что вы работаете с типом String? (String Необязательный или Необязательный String). Фактически, всякий раз, когда вы пытаетесь

print(myString)

во время выполнения консоль отладки напечатает Optional("foobar"). Часть "Optional()" указывает, что эта переменная может иметь или не иметь значения во время выполнения, но в этом случае в настоящее время она содержит строку "foobar". Эта индикация "Optional()" останется, если вы не выполняете так называемое "разворачивание" необязательного значения.

Развертывание необязательно означает, что теперь вы производите этот тип как необязательный. Это создаст новый тип и назначит значение, которое было бы в пределах этого дополнительного для нового необязательного типа. Таким образом, вы можете выполнять операции над этой переменной, так как это было гарантировано компилятором, чтобы иметь твердое значение.


Условно разворачивание будет проверять, является ли значение в необязательном значении nil или нет. Если это не nil, будет создана новая константная переменная, которой будет присвоено значение и разворачивается в необязательную константу. И оттуда вы можете безопасно использовать необязательный элемент в блоке if.

Примечание. Вы можете предоставить условно развернутую константу с тем же именем, что и необязательная переменная, которую вы разворачиваете.

if let myString = myString {
    print(myString)
    // will print "foobar"
}

Условные разворачивающиеся опции - это самый чистый способ получить доступ к необязательному значению, потому что если он содержит значение nil, то все в блоке if let не будет выполняться. Конечно, как и любой оператор if, вы можете включить блок else

if let myString = myString {
    print(myString)
    // will print "foobar"
}
else {
    print("No value")
}

принудительная развязка выполняется с использованием так называемого оператора ! ( "bang" ). Это менее безопасно, но при этом позволяет компилировать код. Однако всякий раз, когда вы используете оператор bang, вы должны быть на 1000% уверены, что ваша переменная действительно содержит твердое значение перед принудительной разворачиванием.

var myString: String?

myString = "foobar"

print(myString!)

Это вышеописанный код Swift. Он печатает значение myString, которое было установлено как "foobar". Пользователь увидит foobar, напечатанный в консоли, и об этом. Но пусть предположить, что значение никогда не было установлено:

var myString: String?

print(myString!) 

Теперь у нас другая ситуация в наших руках. В отличие от Objective-C, всякий раз, когда делается попытка принудительно развернуть необязательный параметр, а необязательный параметр не был установлен и nil, когда вы пытаетесь развернуть опцию, чтобы увидеть, что внутри вашего приложения произойдет сбой.


Развертка с литьем. Как мы уже говорили ранее, в то время как вы unwrapping необязательно, что вы на самом деле выполняете нестационарный тип, вы также можете отбрасывать необязательный тип для другого типа. Например:

var something: Any?

Где-то в нашем коде переменная something будет установлена ​​с некоторым значением. Возможно, мы используем дженерики, или, может быть, существует другая логика, которая заставит это измениться. Поэтому в нашем коде мы хотим использовать something, но по-прежнему можем относиться к нему иначе, если это другой тип. В этом случае вам нужно использовать ключевое слово as, чтобы определить это:

Примечание. Оператором as является тип ввода в Swift.

// Conditionally
if let thing = something as? Int {
    print(thing) // 0
}

// Optionally
let thing = something as? Int
print(thing) // Optional(0)

// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised

Обратите внимание на разницу между двумя ключевыми словами as. Как и раньше, когда мы принудительно разворачивали опциональный, мы использовали оператор ! bang для этого. Здесь вы сделаете то же самое, но вместо того, чтобы кастовать как просто не факультативный, вы также набрасываете его как Int. И он должен быть в состоянии быть downcast как Int, в противном случае, как использование оператора bang, когда значение nil, ваше приложение выйдет из строя.

И чтобы использовать эти переменные вообще в какой-то или математической операции, они должны быть развернуты для этого.

Например, в Swift могут использоваться только действительные типы данных такого же типа. Когда вы вводите тип с помощью as!, вы вынуждаете downcast этой переменной, как если бы вы были определенными, это тот тип, поэтому он безопасен для работы и не разбивает ваше приложение. Это нормально, пока переменная действительно относится к типу, на который вы его производите, иначе у вас будет беспорядок на руках.

Тем не менее кастинг с помощью as! позволит компилировать ваш код. Кастинг с помощью as? - это совсем другая история. Фактически, as? объявляет ваш Int как совершенно другой тип данных.

Теперь это Optional(0)

И если вы когда-либо пытались сделать домашнее задание, пишите что-то вроде

1 + Optional(1) = 2

Ваш учитель математики наверняка дал бы вам "F". То же самое с Свифт. Кроме Свифта, скорее всего, не будет компилироваться, а не дать вам оценку. Потому что в конце дня опционально может быть nil.

Безопасность первых детей.

Ответ 4

'?' означает опциональную цепочку выражения

значение "!" означает силу-значение
введите описание изображения здесь