Що таке "розгорнуте значення" у Swift?


155

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

Працюючи з необов’язковими значеннями, ви можете писати? перед такими операціями, як методи, властивості та підписка. Якщо значення перед? нуль, все після? ігнорується, а значення цілого виразу дорівнює нулю. В іншому випадку необов'язкове значення розкручується , і все після? діє на нерозкрите значення . В обох випадках значення цілого виразу є необов’язковим значенням.

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

Я не отримую цього, і шукаю в Інтернеті без везіння.

Що це означає?


Редагувати

З відповіді Сезарі є невелика різниця між виведенням оригінального коду та кінцевим рішенням (випробуваним на майданчику):

Оригінальний код

Оригінальний код

Розчин Цезарі

Розчин Цезарі

Властивості суперкласу відображаються у висновку у другому випадку, тоді як у першому випадку є порожній об'єкт.

Хіба результат не повинен бути однаковим в обох випадках?

Питання та відповіді: Що таке необов'язкове значення у Swift?


1
Відповідь Цезарі - це місце. Також на iBooks БЕЗКОШТОВНО
Daniel Storm

@DanielStormApps Цей iBook є портативною версією пов'язаного підручника в моєму запитанні :)
Bigood

Відповіді:


289

По-перше, ви повинні зрозуміти, що таке необов’язковий тип. Необов'язковий тип в основному означає, що змінна може бути 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

Редагувати:

Різниця, яку ви бачите, полягає саме в тому, що факультативне значення завершено . Зверху є ще один шар. Розгорнута версія тільки показує прямий об'єкт , тому що, ну, розгорнута.

Швидке порівняння майданчика:

дитячий майданчик

У першому та другому випадках об'єкт не розкручується автоматично, тому ви бачите два "шари" ( {{...}}), тоді як у третьому випадку ви бачите лише один шар ( {...}), оскільки об'єкт автоматично розкручується.

Різниця між першим і другим двома випадками полягає в тому, що два інші випадки дадуть вам помилку виконання, якщо optionalSquareвстановлено значення nil. Використовуючи синтаксис у першому випадку, ви можете зробити щось подібне:

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

1
Дякую за чітке пояснення! Код, включений у моє запитання, цитується з підручника Apple (посилання на запитання), і я думаю, що він повинен працювати так, як є (і він робить). Хоча ваше остаточне рішення та оригінальне рішення дають різні результати (див. Мою редакцію). Будь-яка думка?
Бігуд

2
Класно. Чудове пояснення. Я все ще розгублений. Чому я хочу використовувати необов’язкове значення? Коли це корисно? Чому Apple створила таку поведінку та синтаксис?
Тодд Леман

1
Це особливо актуально, коли мова йде про властивості класу. Swift вимагає ініціалізації всіх необов'язкових методів у init()функції класу. Я рекомендую прочитати глави "Основи" (охоплює необов'язкові), "Ініціалізація" та "Необов'язковий ланцюжок" у книзі Swift, опублікованій Apple.
Чезарі Войцік

2
@ToddLehman Apple створила це, щоб допомогти вам написати набагато безпечніший код. Наприклад, уявіть всі винятки нульових покажчиків у програмі Java, яка завершує роботу програми, оскільки хтось забув перевірити наявність null. Або дивна поведінка на Objective-C, тому що ви забули перевірити наявність нуля. З необов'язковим типом ви не можете забути мати справу з нулем. Компілятор змушує вас з цим впоратися.
Ерік Енгхайм

5
@AdamSmith - Виходячи з фону Java, я напевно бачу використання додаткових опцій ... але, поступаючи також (останнім часом) з фону Objective-C, у мене все ще виникають проблеми з його використанням, оскільки C явно дозволяє надсилати повідомлення об’єктам з нульовою силою. Хм. Мені б хотілося, щоб хтось писав приклад із таких показів, як вони допомагають зробити код коротшим та безпечнішим, ніж еквівалентний код, який не використовує їх. Я не кажу, що вони не корисні; Я просто кажу, що ще не "зрозумію".
Тодд Леман

17

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

Отже, дозвольте мені допомогти тим, хто розробляє "правильний розум" (візуального мислення), надавши іншу точку зору на додаток до правильної відповіді. Ось гарна аналогія, яка мені дуже допомогла.

Аналогія подарунка до дня народження

Подумайте про необов'язкові речі як на подарунки на день народження, які виходять в жорсткій, твердій, кольоровій упаковці.

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

Якщо значення необов'язкового немає nil, тепер ви відкрили поле, що містить щось . Але, особливо якщо значення не введено явно і є змінною, а не заздалегідь визначеною константою, тоді вам може знадобитися відкрити вікно, перш ніж ви зможете дізнатися що-небудь конкретне про те, що знаходиться у полі, наприклад, що це за тип чи що фактичне значення .

Що в коробці ?! Аналогія

Навіть після розгортання змінної ви все ще схожі на Бреда Пітта в останній сцені в SE7EN ( попередження : спойлери та дуже нецензурна лайка та насильство), тому що навіть після того, як ви розгорнули присутні, ви опинитесь у такій ситуації: у вас зараз є 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)

приємно 😊 😊😊 😊 😊😊 😊 😊😊
SamSol

любимо аналогію! Отже, чи є кращий спосіб розгортати коробки? у прикладі коду
Девід Т.

Кішка Шредінгера знаходиться в коробці
Джексонкр

Дякую, що заплутали мене .... Який програміст дарує подарунок на день народження, який все одно порожній? свого роду середнє
delta2flat

@Jacksonkr Або ні.
amsmath

13

Swift ставить високу плату за безпеку типу. Вся мова Swift була розроблена з урахуванням безпеки. Це одна з характерних ознак Свіфта і така, яку слід зустріти з розпростертими обіймами. Це допоможе розробити чистий, читабельний код та допоможе уникнути збоїв у вашій програмі.

Усі додаткові опції в Swift розмежовані ?символом. Встановивши ?ім'я типу після того, як ви оголошуєте його як необов’язковий, ви по суті виводите це не як тип, який знаходиться перед ?, а замість цього як необов'язковий тип.


Примітка: Змінний або типу Intє НЕ таким же , як Int?. Вони є двома різними типами, які не можна керувати один одним.


Використання за бажанням

var myString: String?

myString = "foobar"

Це не означає, що ви працюєте з типом String. Це означає, що ви працюєте з типом String?(String необов’язковий або Факультативний рядок). Насправді, коли ви намагаєтесь

print(myString)

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

Розгортання необов’язкового означає, що зараз ви видаєте цей тип як необов'язковий. Це створить новий тип і призначить нове необов'язкове тип значення, яке знаходилось в межах цього факультативного. Таким чином ви можете виконувати операції над цією змінною, оскільки компілятор гарантував її тверде значення.


Умовно Unwrapping перевіряє, чи є значення в необов'язковому nilчи ні. Якщо це не так nil, з'явиться нещодавно створена константа змінної, якій буде присвоєно значення і розгорнуто в необов'язковий константа. І звідти ви можете сміливо використовувати необов'язкове в ifблоці.

Примітка. Ви можете надати умовно розгорнутій константі те саме ім'я, що і необов'язкова змінна, яку ви розгортаєте.

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

Умовно розгортання необов'язкових - це найчистіший спосіб отримати значення необов’язкового, оскільки якщо воно містить значення нуля, то все, що знаходиться в блоці if let, не буде виконуватися. Звичайно, як і будь-який вислів if, ви можете включати ще блок

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

Примусово розгортання виконується за допомогою використання !оператора, який відомий як ("удар"). Це менш безпечно, але все ж дозволяє компілювати ваш код. Однак кожен раз, коли ви використовуєте оператор вибуху, ви повинні бути на 1000% впевнені, що ваша змінна насправді містить суцільне значення перед примусовим розкручуванням.

var myString: String?

myString = "foobar"

print(myString!)

Це вище є повністю дійсним кодом Swift. Він виводить значення, myStringяке було встановлено як "foobar". Користувач побачив би foobarнадруковані в консолі, і це про це. Але припустимо, що значення ніколи не було встановлено:

var myString: String?

print(myString!) 

Зараз у нас на руках інша ситуація. На відміну від Objective-C, кожного разу, коли робиться спроба примусово розгортати необов'язковий, і необов'язковий не встановлено, і це робиться nil, коли ви намагаєтесь розгортати необов'язковий, щоб побачити, що всередині вашої програми стане збоєм.


Розгортання w / тип лиття . Як ми вже говорили раніше, якщо ви є unwrappingнеобов'язковим, ви фактично перетворюєте його на необов'язковий тип, ви також можете передавати необов'язковий іншому типу. Наприклад:

var something: Any?

Десь у нашому коді змінна somethingбуде встановлена ​​з деяким значенням. Можливо, ми використовуємо дженерики або, можливо, існує якась інша логіка, яка спричинить це до зміни. Тож пізніше в нашому коді ми хочемо використовувати, somethingале все-таки зможемо по-різному ставитися до нього, якщо це інший тип. У цьому випадку ви хочете використовувати asключове слово, щоб визначити це:

Примітка. asОператор - це те, як ви вводите роль "Свіфт".

// 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". Те саме зі Свіфтом. За винятком того, що Свіфт швидше взагалі не збирає, а не дає оцінку. Тому що наприкінці дня необов'язковий може насправді бути нульовим .

Безпека перших дітей.


Приємне пояснення факультативних типів. Допоміг зрозуміти мені речі.
Tobe_Sta

0

'?' означає необов'язковий ланцюговий вираз

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

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