Что означает восклицательный знак на языке Swift?

518

Руководство по языку программирования Swift содержит следующий пример:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { println("\(name) is being deinitialized") }
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { println("Apartment #\(number) is being deinitialized") }
}

var john: Person?
var number73: Apartment?

john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)

//From Apple's “The Swift Programming Language” guide (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)

Затем, при назначении квартиры человеку, они используют восклицательный знак, чтобы «развернуть экземпляр»:

john!.apartment = number73

Что значит «развернуть экземпляр»? Зачем это нужно? Чем он отличается от простого:

john.apartment = number73

Я очень новичок в языке Swift. Просто пытаюсь понять основы.


ОБНОВЛЕНИЕ:
Большая часть загадки, которую я пропустил (прямо не указано в ответах - по крайней мере, на момент написания этой статьи), заключается в том, что когда вы делаете следующее:

var john: Person?

это НЕ означает, что « johnимеет тип, Personи это может быть ноль», как я изначально думал. Я просто неправильно это понял Personи Person?совершенно разные типы. Как только я понял это, все остальное ?, !безумие и великие ответы, приведенные ниже, стали намного более понятными.

Трой
источник

Ответы:

530

Что значит «развернуть экземпляр»? Зачем это нужно?

Насколько я могу потренироваться (это тоже очень ново для меня) ...

Термин «завернутый» подразумевает, что мы должны думать о необязательной переменной как о подарке, завернутом в блестящую бумагу, которая может (к сожалению!) Быть пустой .

Когда "обернуто", значение необязательной переменной является перечислением с двумя возможными значениями (немного как логическое значение). Это перечисление описывает, содержит ли переменная значение ( Some(T)) или нет ( None).

Если есть значение, его можно получить, "развернув" переменную (получив Tиз Some(T)).

Чем john!.apartment = number73отличается от john.apartment = number73? (Перефразировать)

Если вы пишете имя необязательной переменной (например, текст john, без знака !), это относится к «перенесенному» перечислению (Some / None), а не к самому значению (T). Так johnчто не является экземпляром Person, и у него нет apartmentчлена:

john.apartment
// 'Person?' does not have a member named 'apartment'

Фактическое Personзначение может быть развернуто различными способами:

  • «принудительная распаковка»: john!(дает Personзначение, если оно существует, ошибка времени выполнения, если оно равно нулю)
  • «необязательное связывание»: if let p = john { println(p) }(выполняет, printlnесли значение существует)
  • «необязательное сцепление»: john?.learnAboutSwift()(выполняет этот готовый метод, если значение существует)

Я предполагаю, что вы выбираете один из этих способов развернуть, в зависимости от того, что должно произойти в нулевом случае, и насколько это вероятно. Эта языковая схема вынуждает обрабатывать нулевой случай явно, что, я полагаю, повышает безопасность по сравнению с Obj-C (где легко забыть обработать нулевой случай).

Обновление :

Восклицательный знак также используется в синтаксисе для объявления «Неявно развернутые дополнительные компоненты».

В приведенных выше примерах johnпеременная была объявлена ​​как var john:Person?, и она является необязательной. Если вы хотите фактическое значение этой переменной, вы должны развернуть его, используя один из трех методов выше.

Если бы оно было объявлено как var john:Person!вместо, переменная была бы неявно развернутой опцией (см. Раздел с этим заголовком в книге Apple). Нет необходимости распаковывать переменные такого типа при доступе к значению, и johnих можно использовать без дополнительного синтаксиса. Но книга Apple гласит:

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

Обновление 2 :

Статья Майка Эша « Интересные быстрые особенности » дает некоторые мотивы для дополнительных типов. Я думаю, что это здорово, четкое написание.

Обновление 3 :

Еще одна полезная статья о неявно развернутом необязательном использовании восклицательного знака: « Свифт и последняя миля » Криса Адамсона. В статье объясняется, что это прагматическая мера Apple, используемая для объявления типов, используемых их средами Objective C, которые могут содержать ноль. Объявление типа как необязательного (с использованием ?) или неявно развернутого (с использованием !) является «компромиссом между безопасностью и удобством». В примерах, приведенных в статье, Apple решила объявить типы как неявно развернутые, что делает вызывающий код более удобным, но менее безопасным.

Возможно, в будущем Apple может пролистать свои структуры, устранив неопределенность неявно развернутых («вероятно, никогда ноль») параметров и заменив их необязательными («безусловно, может быть ноль в конкретных [надеюсь, задокументированных!] Обстоятельствах») или стандартными не -опциональные ("никогда не ноль") объявления, основанные на точном поведении их кода Objective-C.

Ashley
источник
Я не уверен в этом объяснении. Если вы просто запустите код без! это все еще возвращает фактическое значение. Может быть! для скорости?
Ричард Вашингтон
ХОРОШО. Так что документы говорят об использовании! когда вы точно знаете, что это может быть развернуто. Но вы можете нормально выполнить код без него (четвертый вариант для вашего списка - неявное развертывание) И без предварительной проверки. Вы возвращаете значение или ноль, если ноль. Но если вы точно знаете, что это не ноль, тогда используйте! ...... но я все еще не понимаю, зачем вам это делать?
Ричард Вашингтон
2
Привет @RichardWashington - я добавил в свой ответ обновление, которое, надеюсь, прояснит кое-что из этого.
Эшли
Обновление 3 именно то, что я ищу. Перед прочтением я подумал, что Apple лжет, когда они возвращают что-то вроде NSURLCredential! который на самом деле может быть ноль.
Золотой большой палец
Спасибо за фантастический ответ @Ashley. После прочтения этого ответа и некоторых примеров быстрого кода Apple, мне действительно кажется, что в этом компромиссе между безопасностью и производительностью обычно лучше быть на более безопасной стороне. Я обнаружил заметное исключение, когда Apple использует принудительную распаковку для элементов пользовательского интерфейса, что имеет смысл, поскольку, во-первых, элементы пользовательского интерфейса обычно являются необязательными при использовании раскадровки (поскольку им не назначается значение программно), а во-вторых, потому что они почти никогда не ноль, если их контроллер представления не ноль, и в этом случае это обсуждение, если спорный.
Михир
126

Вот в чем я думаю разница:

var john: Person?

Значит джон может быть ноль

john?.apartment = number73

Компилятор интерпретирует эту строку как:

if john != nil {
    john.apartment = number73
}

Пока

john!.apartment = number73

Компилятор интерпретирует эту строку просто:

john.apartment = number73

Следовательно, используя! развернет оператор if и заставит его работать быстрее, но если john равен nil, произойдет ошибка времени выполнения.

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

Обновить:

Возвращаясь к этому ответу через 4 года, так как я получил наибольшую репутацию от него в Stackoverflow :) Я немного не понял смысл распаковки в то время. Теперь, спустя 4 года, я считаю, что смысл развертывания здесь состоит в том, чтобы расширить код от его первоначальной компактной формы. Также это означает устранение неопределенности вокруг этого объекта, поскольку мы не уверены, что по определению он равен нулю или нет. Как и ответ Эшли выше, подумайте о нем как о подарке, в котором ничего не может быть. Но я все еще думаю, что развертывание - это развертывание кода, а не развертывание на основе памяти, как при использовании enum.

Amr
источник
9
На моей игровой площадке john.apartment = number73 не компилируется, вы должны указать john? .Apartment = number73
Чак Пинкерт
2
@ChuckPinkert прав, 4-ю строку нужно отредактировать как john? .Apartment = number73, хороший ответ, хотя!
Брюс
john.apartment = number73 выдает ошибку: значение необязательного типа 'Person?' не развернутый: вы хотели использовать '!' или '?'?
Человек-паук
4
Развертывание не имеет ничего общего с производительностью. «Проверка на ноль» по-прежнему должна выполняться во время выполнения, единственное отличие состоит в том, что будет выдана ошибка времени выполнения, если в Optional не указано значение.
Madidier
67

TL; DR

Что означает восклицательный знак на языке Swift?

Восклицательный знак фактически говорит: «Я знаю, что эта опция определенно имеет значение; пожалуйста, используйте это. ” Это называется принудительным развертыванием значения необязательного параметра:

пример

let possibleString: String? = "An optional string."
print(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."

let assumedString: String! = "An implicitly unwrapped optional string."
print(assumedString)  // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

Источник: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399

Алекс Ноласко
источник
11
Ваш ответ великолепен, потому что я понимаю, что происходит сейчас. Чего я не понимаю, так это почему существуют неявно развернутые опции. Зачем создавать что-то определенное как неявно развернутый необязательный тип String, а не как обычный тип String? Их использование после того же. Что мне не хватает?
Пачун
Это больше не похоже на правду. В Swift 5 Repl, если я делаю, let f: String! = "hello"а затем print(f), вывод Optional("hello")вместо просто "hello".
номер
37

Если бы john был необязательным var (объявленным так)

var john: Person?

тогда Джон мог бы не иметь значения (на языке ObjC, значение ноль)

Восклицательный знак в основном говорит компилятору: «Я знаю, что это имеет значение, вам не нужно проверять его». Если вы не хотите использовать его, вы можете условно проверить его:

if let otherPerson = john {
    otherPerson.apartment = number73
}

Интерьер этого будет только оценить, если Джон имеет значение.

Бен Готлиб
источник
4
Спасибо за ответ, теперь я понимаю, что говорит восклицательный знак, я все еще пытаюсь понять, почему ... Вы сказали, что это говорит компилятору: «Я знаю, что это имеет значение, вам не нужно проверять Это". Что он покупает, когда компилятор не проверяет его? Без восклицательного знака, собирается ли компилятор выдать ошибку (у меня еще нет среды, где я могу протестировать Swift)? Это ! 100% необходимо для всех необязательных переменных? Если так, то почему Apple беспокоится об этом, а не просто делает так, что не хватает ! означает «Я знаю, что это имеет значение, вам не нужно проверять это»?
Трой
«компилятор собирается выдать ошибку» - нет, он все еще работает нормально, возвращая значение, как ожидалось. Я не понимаю этого. Это ! только для скорости, когда ты уверен, может быть?
Ричард Вашингтон
На самом деле позже документы расскажут о неявно развернутых опциях, используя приведенный ниже пример: “let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.” Но он отлично работает без! Здесь что-то странное
Ричард Вашингтон
3
@RichardWashington Ты сбит с толку по уважительной причине! Примеры в документах опускают некоторые вещи и вводят в заблуждение, потому что все, что они делают, это используют println. По-видимому, есть, по крайней мере, пара случаев, когда развёртывание опций не требуется. Один из них при использовании println. Другое - при использовании интерполяции строк. Так, может быть, println и интерполяция строк распаковываются под обложки? Может быть, у кого-то есть больше понимания этого. Просмотр определений бета-кода Xcode6 не раскрыл мне ничего об этой магии.
Mbeaty
Да, я понимаю, John?и Johnэто два разных типа. Один имеет тип Необязательный Персона, а другой - Персона. Необязательное лицо должно быть развернуто, прежде чем вы сможете вывести человека. Но, как вы говорите, кажется, что это происходит, по крайней мере, в этих обстоятельствах без необходимости что-либо делать. Это, кажется, сделать! лишний. Если только ВСЕГДА необязателен, но рекомендуется делать, чтобы отловить ошибки времени компиляции. В некотором роде назначение vars / let для определенного типа может быть явным, let John: Person = ...но также может быть выведено let John = ....
Ричард Вашингтон
27

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

В Swift восклицательный знак появляется в нескольких контекстах:

  • Принудительная распаковка: let name = nameLabel!.text
  • Неявно развернутые опции: var logo: UIImageView!
  • Принудительное литье: logo.image = thing as! UIImage
  • Необработанные исключения: try! NSJSONSerialization.JSONObjectWithData(data, [])

Каждый из них представляет собой отдельную языковую конструкцию с различным значением, но у всех них есть три общие общие черты:

1. Восклицательные знаки обходят проверки безопасности Swift во время компиляции.

Когда вы используете !Swift, вы, по сути, говорите: «Эй, компилятор, я знаю, что вы думаете, что здесь может произойти ошибка , но я знаю с полной уверенностью, что она никогда не будет».

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

Однако всякий раз, когда вы используете ! , вы исключаете наличие пути восстановления для ошибки, что означает, что ...

2. Восклицательные знаки - потенциальные сбои.

Восклицательный знак также гласит: «Эй, Свифт, я настолько уверен, что эта ошибка никогда не произойдет, что для тебя лучше вывести из строя все мое приложение, чем для меня написать код для его восстановления».

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

Тем не менее, когда я вижу !в дикой природе, он редко используется так осознанно. Вместо этого это слишком часто означает: «это значение было необязательным, и я не особо задумывался о том, почему оно может быть нулевым или как правильно справиться с этой ситуацией, но добавляя! делало его компилируемым… так что мой код верен, верно?»

Остерегайтесь высокомерия восклицательного знака. Вместо…

3. Восклицательные знаки лучше всего использовать экономно.

Каждая из этих !конструкций имеет ?аналог, который заставляет вас иметь дело с ошибкой / нулем:

  • Условное развертывание: if let name = nameLabel?.text { ... }
  • Optionals: var logo: UIImageView?
  • Условные приведения: logo.image = thing as? UIImage
  • Исключения без ошибок: try? NSJSONSerialization.JSONObjectWithData(data, [])

Если у вас есть соблазн использовать !, всегда полезно тщательно обдумать, почему вы его не используете ?. Действительно ли сбой вашей программы - лучший вариант, если !операция не удалась? Почему это значение является необязательным / ошибочным?

Есть ли разумный путь восстановления, который ваш код мог бы использовать в случае nil / error? Если так, закодируйте это.

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

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

Я периодически ищу всю свою кодовую базу !и проверяю каждое ее использование. Очень немногие обычаи выдерживают проверку. (На момент написания этой статьи вся структура Siesta имела ровно два экземпляра .)

Это не значит, что вы никогда не должны использовать !в своем коде - просто вы должны использовать его осознанно и никогда не делать его опцией по умолчанию.

Пол Кантрелл
источник
func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { if(receiptData == nil) { return false; } return (hasValidTrial(receiptData!) || isNotExpired(receiptData!)) && isNotCancelled(receiptData!) } Учитывая 3. есть ли лучший способ написать это?
Дженсон Баттон: мероприятие
Можно сказатьfunc isSubscriptionActive(receiptData: NSDictionary?) -> Bool { guard let nonNilReceiptData = receiptData else { return false} return (hasValidTrial(nonNilReceiptData) || isNotExpired(nonNilReceiptData)) && isNotCancelled(nonNilReceiptData) }
rkgibson2
Восклицательные знаки не обходят никакие проверки безопасности. Вы получите гарантированный сбой, если опционально ноль. Необязательный всегда проверяется. Это просто очень жестокий способ проверки безопасности.
gnasher729
@ gnasher729 Как говорится в ответе, он обходит проверки безопасности во время компиляции в пользу сбоя безопасного выполнения .
Пол Кантрелл
24

johnявляется необязательным var. Так может быть содержит nilзначение. Чтобы убедиться, что значение не ноль, используйте !в конце varимени.

Из документации

«Если вы уверены, что необязательный параметр содержит значение, вы можете получить доступ к его базовому значению, добавив восклицательный знак (!) В конце имени необязательного элемента. Восклицательный знак фактически говорит: «Я знаю, что эта опция определенно имеет значение; пожалуйста, используйте это. ”

Еще один способ проверить ненулевое значение

    if let j = json {
        // do something with j
    }
Жарить
источник
1
Да, я видел это в документации, но это все еще кажется ненужным ... поскольку, мне кажется, john.apartment = number73также говорит: "Я знаю, что этот опционально определенно имеет значение; пожалуйста, используйте его." ...
Трой
6
Да, это так, но дело в том, что по умолчанию Swift пытается отлавливать ошибки во время компиляции. Если вы знаете или думаете, что знаете, что эта переменная не может содержать ноль, вы можете удалить эту проверку, используя восклицательный знак.
lassej
16

Вот некоторые примеры:

var name:String = "Hello World"
var word:String?

Где wordэто необязательное значение. означает, что может содержать или не содержать значение.

word = name 

Здесь nameесть значение, чтобы мы могли назначить его

var cow:String = nil
var dog:String!

Где dogпринудительно развернутый означает, что он должен содержать значение

dog = cow

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

Рамкумар Чинтала
источник
1
var c:Int = nilполучит: «Nil не может инициализировать указанный тип« int »»
Asususer
15

В этом случае...

вар Джон: Человек!

это означает, что первоначально John будет иметь значение nil, оно будет установлено, и как только set никогда больше не будет иметь нулевое значение. Поэтому для удобства я могу использовать более простой синтаксис для доступа к необязательному var, потому что это «неявно развернутый необязательный»

гость
источник
1
Спасибо. Я также нашел следующую ссылку, которая объясняет это. Этот тип типа называется «неявно развернутая опция». stackoverflow.com/questions/24122601/…
Кейделл
5

Если вы пришли из языка семейства C, вы будете думать «указатель на объект типа X, который может быть адресом памяти 0 (NULL)», и если вы исходите из языка с динамической типизацией, вы будете думая «Объект, который, вероятно, имеет тип X, но может быть неопределенного типа». Ничто из этого на самом деле не верно, хотя окольным путем первый близок.

То, как вы должны думать об этом, как будто это объект вроде:

struct Optional<T> {
   var isNil:Boolean
   var realObject:T
}

Когда вы тестируете ваше необязательное значение, foo == nilоно действительно возвращается foo.isNil, а когда вы говорите, foo!оно возвращается foo.realObjectс утверждением, что foo.isNil == false. Важно отметить это, потому что если на fooсамом деле ноль, когда вы делаетеfoo! , то это ошибка времени выполнения, поэтому обычно вместо этого вы хотите использовать условное let, если только вы не уверены, что значение не будет nil. Такая хитрость означает, что язык может быть строго типизирован, не заставляя вас проверять, являются ли значения везде нулевыми.

На практике это не так, потому что работа выполняется компилятором. На высоком уровне существует тип, Foo?который является отдельным от него Fooи который не позволяет функциям, принимающим тип, Fooполучать значение nil, но на низком уровне необязательное значение не является истинным объектом, поскольку у него нет свойств или методов; вполне вероятно, что на самом деле это указатель, который может быть равен NULL (0) с соответствующим тестом при принудительном развертывании.

Есть другая ситуация, в которой вы увидите восклицательный знак типа:

func foo(bar: String!) {
    print(bar)
}

Это примерно эквивалентно принятию необязательного с принудительной разверткой, то есть:

func foo(bar: String?) {
    print(bar!)
}

Вы можете использовать это, чтобы иметь метод, который технически принимает необязательное значение, но будет иметь ошибку времени выполнения, если он равен нулю. В текущей версии Swift это, по-видимому, обходит утверждение is-not-nil, поэтому вместо этого у вас будет ошибка низкого уровня. Обычно это не очень хорошая идея, но она может быть полезна при преобразовании кода с другого языка.

Джим Дрисколл
источник
3

Если вы знакомы с C #, это похоже на типы Nullable, которые также объявляются с помощью знака вопроса:

Person? thisPerson;

И восклицательный знак в этом случае эквивалентен доступу к свойству .Value типа NULL, например:

thisPerson.Value
Абдуррахман
источник
2

В целевых C переменные без значения были равны 'nil' (также можно было использовать значения 'nil', такие же, как 0 и false), следовательно, было возможно использовать переменные в условных выражениях (переменные со значениями такие же, как 'TRUE) 'и те, у которых нет значений, были равны' ЛОЖЬ ').

Swift обеспечивает безопасность типов, предоставляя «дополнительное значение». т.е. предотвращает возникновение ошибок при назначении переменных разных типов.

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

var hw = "Hello World"

Здесь, даже несмотря на то, что «hw» является строкой, ее нельзя использовать в операторе if, как в цели C.

//This is an error

if hw

 {..}

Для этого это должно быть создано как,

var nhw : String? = "Hello World"

//This is correct

if nhw

 {..}
Gokul
источник
2

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

cheborneck
источник
2

Коротко (!): После того, как вы объявите переменную и убедитесь, что переменная содержит значение.

let assumedString: String! = "Some message..."
let implicitString: String = assumedString

иначе вам придется делать это на каждом после передачи значения ...

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
Эн Хуэй Лим
источник
1

Джон - необязательный Персона, то есть он может иметь значение или быть нулем.

john.apartment = number73

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

john!.apartment = number73

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

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

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}
Коннор
источник
Вы говорите, что если Джон равен нулю, и я иду john.apartment = number73, это не приведет к ошибке, если я не поставлю '!' после джона?
Трой
Если Джон является необязательным, вы должны использовать восклицательный знак для доступа к его свойствам, потому что компилятор должен знать, что это не ноль. Если это не опционально, вы не можете использовать восклицательный знак.
Коннор
1
Если я должен использовать восклицательный знак для ВСЕХ необязательных переменных, то почему Apple вообще беспокоится об этом, в отличие от того, что отсутствие восклицательного знака означает то же самое? Это кажется ненужным раздуванием ... Или есть случай, когда имеет смысл НЕ использовать восклицательный знак с необязательной переменной var?
Трой
Вы используете восклицательный знак, когда вам нужно, чтобы опциональное значение не было ноль. Как и в случае с доступом к свойствам Джона. Вы могли бы сделать что-то вроде вар Джек: Человек? = Джон, где Джек также необязательный или Вар Джек: Персона = Джон! где Джек Человек, а не ноль
Коннор
Правильно, но в исходном примере разве тот факт, что я разыменовываю johnнеобязательный параметр, не говорит компилятору, что он должен быть ненулевым? ... А в вашем var jack: Person = john!примере, не отсутствие? после Person скажи компилятору, что нужно johnбыть не ноль? В целом, !кажется, что это кажется излишним ... Мне все еще кажется, что я что-то упускаю в части "почему" !...
Трой
1

Проще говоря, восклицательные знаки означают, что необязательный объект разворачивается. Необязательной является переменная, которая может иметь значение или нет - так что вы можете проверить, является ли переменная пустой, используя оператор if let, как показано здесь , и затем принудительно развернуть его. Если вы принудительно развернете необязательную опцию, которая пуста, ваша программа потерпит крах, так что будьте осторожны! Необязательные объявляются путем установки знака вопроса в конце явного присваивания переменной, например, я мог бы написать:

var optionalExample: String?

Эта переменная не имеет значения. Если бы я развернул его, программа вылетала бы, и Xcode сообщал бы вам, что вы пытались развернуть опциональный файл со значением nil.

Надеюсь, что это помогло.

сера
источник
1

В простых словах

ИСПОЛЬЗОВАНИЕ Восклицательный знак указывает, что переменная должна состоять не из нулевого значения (оно никогда не должно быть ноль)

Maninderjit Singh
источник
1

Вся история начинается с функции swift, называемой необязательными переменными. Это переменные, которые могут иметь значение или не иметь значения. В целом, swift не позволяет нам использовать переменную, которая не инициализирована, так как это может привести к сбоям или неожиданным причинам, а также использовать сервер для заполнения бэкдоров. Таким образом, чтобы объявить переменную, значение которой изначально не определено, мы используем '?'. Когда такая переменная объявлена, чтобы использовать ее как часть некоторого выражения, необходимо развернуть их перед использованием, развертывание - это операция, посредством которой обнаруживается значение переменной, это относится к объектам. Без распаковки, если вы попытаетесь использовать их, у вас будет ошибка времени компиляции. Чтобы развернуть переменную, которая является необязательной переменной var, восклицательный знак "!" используется.

Теперь бывают случаи, когда вы знаете, что таким необязательным переменным будут присваиваться значения, например, системой или вашей собственной программой, но через некоторое время, например, выходами пользовательского интерфейса, в такой ситуации вместо объявления необязательной переменной с помощью вопросительного знака «?» мы используем "!".

При этом система знает, что эта переменная объявлена ​​с "!" не является обязательным прямо сейчас и не имеет значения, но получит значение позже в течение своего срока службы.

Таким образом, восклицательный знак содержит два разных применения: 1. Объявить переменную, которая будет необязательной и получит значение определенно позже 2. Развернуть необязательную переменную перед ее использованием в выражении.

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

вишал дхаранкар
источник
1

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

if (myNumber != 3){
 // if myNumber is NOT 3 do whatever is inside these brackets.
)
Энди Лебовиц
источник
1

Необязательная переменная может содержать значение или не

Дело 1: var myVar:String? = "Something"

случай 2: var myVar:String? = nil

теперь, если вы спросите myVar !, вы говорите компилятору вернуть значение в случае 1, он вернет "Something"

в случае 2 произойдет сбой.

Смысл ! mark заставит компилятор возвращать значение, даже если его там нет. вот почему название Force Unwrapping .

Ашиш Писей
источник
@Moritz. Я не знаю, так ли это? Могу ли я ответить, если на вопрос уже дан ответ? Я вызываю какую-то проблему или в моем ответе что-то не так? я не понимаю, почему вы отрицаете, что ответили правильно? Я попытался ответить, основываясь на моем понимании концепции. Я думаю, что некоторые люди найдут это полезным для ясного и легкого объяснения.
Ашиш Писей
@ Мориц Теперь это конструктивно. Вы должны были сказать мне это исправление в первую очередь, если это было причиной. Спасибо за ответ. я исправил это.
Ашиш Писей
0
Simple the Optional variable allows nil to be stored.

var str : String? = nil

str = "Data"

To convert Optional to the Specific DataType, We unwrap the variable using the keyword "!"

func get(message : String){
   return
}

get(message : str!)  // Unwapped to pass as String
Джефф Аян
источник
1
Пожалуйста, не пишите ответы, которые не добавляют ничего, что еще не было охвачено предыдущими ответами.
Далия Прасникар
-1

СПРОСИ СЕБЯ

  • У типа person?есть apartmentчлен / свойство? ИЛИ
  • У типа personесть apartmentчлен / свойство?

Если вы не можете ответить на этот вопрос, тогда продолжайте читать:

Чтобы понять, вам может понадобиться супер базовый уровень понимания дженериков . Смотрите здесь . Многие вещи в Swift написаны с использованием Generics. Дополнительные включены

Код ниже был сделан доступным из этого Стэнфордского видео . Настоятельно рекомендую посмотреть первые 5 минут

Необязательным является enum только с 2 падежами

enum Optional<T>{
    case None
    case Some(T)
}

let x: String? = nil //actually means:

let x = Optional<String>.None

let x :String? = "hello" //actually means:

let x = Optional<String>.Some("hello")

var y = x! // actually means:

switch x {
case .Some(let value): y = value
case .None: // Raise an exception
}

Опциональная привязка:

let x:String? = something
if let y = x {
    // do something with y
}
//Actually means:

switch x{
case .Some(let y): print)(y) // or whatever else you like using 
case .None: break
}

когда вы говорите, var john: Person?вы на самом деле имеете в виду такие:

enum Optional<Person>{
case .None
case .Some(Person)
}

Имеет ли перечисленное выше перечисление какое-либо свойство с именем apartment? Вы видите это где-нибудь? Это не было вовсе! Однако, если вы развернете его, то есть сделаете, person!вы сможете ... то, что он делает под капотом:Optional<Person>.Some(Person(name: "John Appleseed"))


Если бы вы определили var john: Personвместо: var john: Person?тогда вам больше не нужно было бы !использовать, потому что Personсам по себе имеет членapartment


В качестве дальнейшего обсуждения того, почему !иногда не рекомендуется использовать распаковку, посмотрите этот раздел вопросов и ответов.

Мед
источник
2
Использование приведенных выше слайдов, возможно, является нарушением авторских прав: «... Стэнфордский университет сохраняет авторские права на весь контент в нашей коллекции iTunes U». от http://itunes.stanford.edu . Вероятно, лучше ответить своими словами на содержание, которое вы узнали из курса, который, по вашему мнению, отвечает на этот вопрос (и вместо того, чтобы использовать слайды, лучше процитировать соответствующие их части в виде кода со ссылками, естественно).
dfri
2
Ваш аргумент - аргумент в пользу популума . В любом случае, я не верю, что Стэнфорд придет сюда и будет защищать права DMCA , но всегда лучше, если ваши ответы будут вашими словами на основе официальных / действительных источников , а не в форме прямого копирования этих источников, как указано выше; особенно когда мы говорим о защищенных авторским правом слайдах: опять же, лучше указывать содержание слайдов в блоках кода (изображения кода, как правило, запрещены здесь, в SO!).
dfri
1
Мета-пост, на который я ссылался, просто описывает подход SO: к этому: пост, подобный этому, естественно, не будет удален, если какой-то случайный пользователь сообщит о нем как о нарушении авторских прав: только если, например, представитель Stanford. должны были прийти сюда, и поскольку пост, подлежащий удалению, НАСТОЛЬКО должен был обеспечить его соблюдение. Поэтому я написал: « Я не верю, что Стэнфорд приедет сюда и будет защищать права DMCA, ...» . Моя точка зрения выше заключается в том, что я считаю, что это, возможно, нарушение авторских прав (что я считаю неправильным), но, естественно, никто никогда не будет применять это. ...
dfri
1
Но, кроме этого, размещение вопросов или ответов, которые содержат изображения кода, не рекомендуется по ряду причин. Подводя итог: я попытался указать на эти комментарии, что я считаю, что этот ответ в его нынешнем виде не совсем так хорош, как мог бы быть, по двум основным причинам (слайды, защищенные авторским правом, изображение кода). Естественно, никто, включая меня, ничего не сделает с этим, я просто указываю это для дальнейшего использования (или, возможно, призываю вас редактировать этот пост с цитатой по блокам кода). Договорились, iTunes U! :)
dfri
2
@dfri удаленные изображения
Honey