Что такое "развернутая стоимость" в Swift?
Я изучаю Swift для iOS 8 / OSX 10.10, следуя этому руководству, и термин "развернутое значение" используется несколько раз, как в этом параграфе (в разделе " Объекты и класс"):
При работе с дополнительными значениями вы можете написать? до операций, таких как методы, свойства и подписка. Если значение до? это ноль, все после? игнорируется, и значение всего выражения равно нулю. В противном случае необязательное значение разворачивается, и все после? действует на развернутую стоимость. В обоих случаях значение всего выражения является необязательным значением.
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
Я не понимаю, и искал в Интернете без удачи.
Что это значит?
редактировать
Из ответа Цезария есть небольшая разница между выводом исходного кода и окончательным решением (проверено на игровой площадке):
Оригинальный код
Решение Цезари
Свойства суперкласса отображаются в выходных данных во втором случае, тогда как в первом случае есть пустой объект.
Разве результат не должен быть одинаковым в обоих случаях?
Связанные вопросы и ответы: Что такое необязательное значение в Swift?
5 ответов
Во-первых, вы должны понять, что такое необязательный тип. Необязательный тип в основном означает, что переменная может быть 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
Sidenote:
Вы также можете объявить необязательные варианты автоматического развертывания, используя восклицательный знак вместо знака вопроса.
Пример:
var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed
Итак, альтернативный способ исправить ваш код:
let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare.sideLength
РЕДАКТИРОВАТЬ:
Разница, которую вы видите, является как раз признаком того факта, что необязательное значение обернуто. Существует еще один слой поверх него. Развернутая версия просто показывает прямой объект, потому что он, ну, в общем, развернутый.
Быстрое сравнение детской площадки:
В первом и втором случаях объект не разворачивается автоматически, поэтому вы видите два "слоя" ({{...}}
), тогда как в третьем случае вы видите только один слой ({...}
) потому что объект автоматически разворачивается.
Разница между первым и вторым двумя случаями заключается в том, что вторые два случая приведут к ошибке времени выполнения, если optionalSquare
установлен в nil
, Используя синтаксис в первом случае, вы можете сделать что-то вроде этого:
if let sideLength = optionalSquare?.sideLength {
println("sideLength is not nil")
} else {
println("sidelength is nil")
}
Существующий правильный ответ великолепен, но я обнаружил, что для полного понимания этого мне нужна хорошая аналогия, поскольку это очень абстрактная и странная концепция.
Итак, позвольте мне помочь тем коллегам-разработчикам "правильного мышления" (визуального мышления), предоставив другую точку зрения в дополнение к правильному ответу. Вот хорошая аналогия, которая мне очень помогла.
Подарочная упаковка
Думайте об опциях как о подарках на день рождения, которые идут в жесткой, твердой цветной упаковке.
Вы не знаете, есть ли что-нибудь внутри упаковки, пока вы не развернете подарок - может быть, внутри ничего нет! Если внутри что-то есть, это может быть еще один подарок, который также обернут, и который также может не содержать ничего. Вы могли бы даже развернуть 100 вложенных подарков, чтобы наконец обнаружить, что нет ничего, кроме упаковки.
Если значение необязательное не nil
Теперь вы обнаружили коробку с чем-то. Но, особенно если значение не указано явно и является переменной, а не предопределенной константой, вам все равно может потребоваться открыть окно, прежде чем вы сможете узнать что-то конкретное о том, что находится в поле, например, какой это тип или что фактическое значение
Что в коробке?! аналогия
Даже после того, как вы развернули переменную, вы все равно похожи на Брэда Питта в последней сцене в SE7EN (предупреждение: спойлеры и ненормативная лексика и насилие с очень высокой оценкой R), потому что даже после того, как вы развернули настоящее, вы попадаете в следующую ситуацию: теперь у вас есть 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)
Swift высоко ценит безопасность типов. Весь язык Swift был разработан с учетом безопасности. Это одна из отличительных черт Swift, которую вы должны приветствовать с распростертыми объятиями. Это поможет в разработке чистого, читабельного кода и поможет предотвратить сбой вашего приложения.
Все опции в Swift разграничены с помощью ?
условное обозначение. Установив ?
после имени типа, который вы объявляете необязательным, вы по сути приводите его не к типу, который находится перед ?
, но вместо этого в качестве необязательного типа.
Примечание: переменная или тип
Int
это не то же самое, чтоInt?
, Это два разных типа, которые нельзя эксплуатировать друг на друге.
Использование опционально
var myString: String?
myString = "foobar"
Это не означает, что вы работаете с типом String
, Это означает, что вы работаете с типом String?
(Строка Необязательная или Необязательная строка). На самом деле, когда вы пытаетесь
print(myString)
во время выполнения консоль отладки напечатает Optional("foobar")
, " Optional()
"part указывает на то, что эта переменная может иметь или не иметь значение во время выполнения, но именно так в настоящее время содержится строка"foobar". This" Optional()
"Индикация останется, если вы не сделаете то, что называется" развертывание "необязательного значения.
Развертывание необязательного означает, что вы используете этот тип как необязательный. Это создаст новый тип и присвоит значение, которое находилось в этом необязательном, новому необязательному типу. Таким образом, вы можете выполнять операции с этой переменной, поскольку компилятор гарантированно имеет твердое значение.
Условно Unwrapping проверит, является ли значение в дополнительном 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, вы должны быть уверены на 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
ключевые слова. Как и раньше, когда мы принудительно развернули дополнительный файл, мы использовали !
оператор взрыва, чтобы сделать это. Здесь вы будете делать то же самое, но вместо того, чтобы разыгрывать как необязательное, вы разыгрываете его также как Int
, И это должно быть в состоянии быть подавленным как Int
в противном случае, как использование оператора взрыва, когда значение nil
Ваше приложение потерпит крах.
И для того, чтобы использовать эти переменные вообще в некоторой сортировке или математической операции, они должны быть развернуты, чтобы сделать это.
Например, в Swift только допустимые числовые типы данных одного типа могут работать друг с другом. Когда вы разыгрываете тип с as!
вы принудительно понижаете значение этой переменной, как если бы вы были уверены, что она принадлежит к этому типу, поэтому она безопасна для работы и не приводит к сбою приложения. Это нормально, если переменная действительно того типа, к которому вы ее приводите, иначе у вас будет беспорядок.
Тем не менее, кастинг с as!
позволит ваш код для компиляции. Кастинг с as?
это другая история. По факту, as?
объявляет ваш Int
как совершенно другой тип данных все вместе.
Теперь есть Optional(0)
И если вы когда-нибудь пытались сделать домашнее задание, написав что-то вроде
1 + Optional(1) = 2
Ваш учитель математики, вероятно, дал бы вам "F". То же самое со Свифтом. За исключением того, что Свифт предпочел бы вообще не компилировать, а не давать вам оценку. Потому что в конце дня необязательный может быть на самом деле ноль.
Безопасность First Kids.
В Swift есть 7 способов развернуть необязательное значение.
- Дополнительное связывание - безопасно
- Дополнительное соединение - безопасно
- Дополнительный шаблон - безопасный
- Nil Coalescing - безопасно
- Заявление о защите - Сейф
- Принудительное разворачивание - небезопасно
- Неявная распаковка - небезопасно