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

Руководство по языку программирования 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? - совершенно разные типы. Как только я это понял, все остальные ?, ! безумие и замечательные ответы, приведенные ниже, стали иметь гораздо больший смысл.


person Troy    schedule 03.06.2014    source источник


Ответы (23)


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

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

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

В «обернутом» состоянии значение необязательной переменной представляет собой перечисление с двумя возможными значениями (немного похоже на логическое значение). Это перечисление описывает, содержит ли переменная значение (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, если значение существует)
  • "optional chaining": john?.learnAboutSwift() (выполняет этот выдуманный метод, если значение существует)

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

Обновление:

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

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

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

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

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

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

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

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

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

person Ashley    schedule 03.06.2014
comment
Я не уверен в этом объяснении. Если вы просто запустите код без! он по-прежнему возвращает фактическое значение. Может быть! это для скорости? - person Richard Washington; 07.06.2014
comment
OK. Итак, в документации говорится об использовании! когда вы точно знаете, что его можно развернуть. Но вы можете нормально запустить код и без него (четвертый вариант для вашего списка - неявное развертывание) И без предварительной проверки. Вы возвращаете значение или ноль, если ноль. Но если вы точно знаете, что это не ноль, тогда используйте! ...... но я все еще не понимаю, зачем вы это делаете? - person Richard Washington; 07.06.2014
comment
Привет, @RichardWashington! Я добавил обновление к своему ответу, которое, надеюсь, проясняет некоторые из этих вопросов. - person Ashley; 09.06.2014
comment
Обновление 3 - это именно то, что я ищу. Перед тем, как его прочитать, я подумал, что Apple лжет, когда они вернули что-то вроде NSURLCredential! что на самом деле могло быть равно нулю. - person Golden Thumb; 16.07.2014
comment
Спасибо за фантастический ответ @Ashley. После прочтения этого ответа и некоторых примеров быстрого кода Apple мне действительно кажется, что в этом компромиссе между безопасностью и производительностью обычно лучше быть на более безопасной стороне. Заметное исключение, которое я обнаружил, - это когда Apple использует принудительное развертывание для элементов пользовательского интерфейса, что имеет смысл, потому что, во-первых, элементы пользовательского интерфейса обычно необязательны при использовании раскадровки (поскольку им не присваивается значение программно), а во-вторых, потому что они почти никогда не nil, если их содержащий контроллер представления не равен nil, и в этом случае это обсуждение спорно. - person Mihir; 24.07.2014
comment
Вот еще один способ подумать об этом: допустим, Эми хочет подтвердить, что у нее есть номер Джона в ее телефоне. Для этого она ищет имя Джона в телефонной книге. Если его там нет, она может сделать одно из двух: она может предположить, что номер, который у нее есть, отличается от номера Джона, или она может впасть в панику, потому что ЗНАЛА, что Джон был в телефонной книге. - person moonman239; 22.04.2015
comment
Кажется, что ! бессмысленен, если он не используется в объявленном ? var, я думаю, что они могли бы упростить задачу, не требуя объявлять необязательный var с ! и просто сделав его неявным, как и любой другой язык - person Dominic; 07.09.2015

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

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.

person Amr    schedule 20.06.2014
comment
У меня на детской площадке john.apartment = number73 не компилируется, необходимо указать john? .Apartment = number73 - person Chuck Pinkert; 24.02.2015
comment
@ChuckPinkert прав, четвертую строку нужно отредактировать на john? .Apartment = number73, хотя ответ хороший! - person Bruce; 06.06.2015
comment
john.apartment = number73 дает ошибку: значение необязательного типа «Человек?» не развернутый: вы хотели использовать '!' или '?'? - person spiderman; 26.06.2015
comment
Распаковка не имеет ничего общего с производительностью. Проверка nil по-прежнему должна выполняться во время выполнения, с той лишь разницей, что во время выполнения будет выдана ошибка, если в параметре Optional нет значения. - person madidier; 07.06.2016

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

person Alex Nolasco    schedule 17.07.2014
comment
Ваш ответ прекрасен, потому что я понимаю что происходит сейчас. Я не понимаю, почему существуют неявно развернутые опции. Зачем создавать что-то, определяемое как неявно развернутую необязательную строку, а не как обычный тип String? Их использование после этого такое же. Что мне не хватает? - person pachun; 10.08.2015
comment
Это больше не похоже на правду. В ответе Swift 5, если я сделаю let f: String! = "hello", а затем print(f), на выходе будет Optional("hello"), а не просто "hello". - person numbermaniac; 09.05.2020

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

var john: Person?

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

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

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

Внутренняя часть этого будет оцениваться только в том случае, если у john есть значение.

person Ben Gottlieb    schedule 03.06.2014
comment
Спасибо за ответ, теперь я понимаю что восклицательный знак, я все еще пытаюсь понять почему ... Вы сказали, что он сообщает компилятору, что я знаю это имеет значение, вам не нужно его проверять. Что он мне дает, если компилятор этого не проверяет? Без восклицательного знака компилятор выдаст ошибку (у меня еще нет среды, где я могу протестировать Swift)? Это ! 100% необходимо для всех необязательных варов? Если так, то почему Apple заморачивалась с этим, вместо того, чтобы просто избавиться от недостатка! означает, что я знаю, что это имеет значение, вам не нужно проверять это? - person Troy; 03.06.2014
comment
компилятор выдаст ошибку - нет, он по-прежнему отлично работает, возвращая ожидаемое значение. Я этого не понимаю. Это ! просто ради скорости, когда ты уверен, что возможно? - person Richard Washington; 07.06.2014
comment
Фактически, позже в документации будет рассказано о неявно развернутых опциях, используя приведенный ниже пример: “let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.” Но он отлично работает без!. Что-то здесь кажется странным. - person Richard Washington; 07.06.2014
comment
@RichardWashington Вы не зря запутались! Примеры в документации упускают некоторые вещи и вводят в заблуждение, потому что все, что они делают, это используют println. Судя по всему, есть как минимум пара случаев, когда разворачивание опций не требуется. Один из них - при использовании println. Другой - при использовании строковой интерполяции. Так, может быть, println и строковая интерполяция разворачиваются под покровом? Может быть, у кого-то есть более глубокое понимание этого. Просмотр определений заголовков Xcode6 Beta ничего не сказал мне об этой магии. - person mbeaty; 07.06.2014
comment
Да, я понимаю, что John? и John - это два разных типа. Один относится к типу «Необязательное лицо», а другой - к типу «Человек». Необязательный Person необходимо развернуть, прежде чем вы сможете вывести Person. Но, как вы говорите, похоже, что это происходит, по крайней мере, в этих обстоятельствах, и при этом фактически ничего не нужно делать. Это похоже на то, чтобы! избыточный. Если только! ВСЕГДА необязательно, но рекомендуется делать, чтобы отловить ошибки времени компиляции. Подобное присваиванию vars / let определенному типу может быть явным let John: Person = ..., но также может быть выведено let John = .... - person Richard Washington; 07.06.2014

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

person Paul Cantrell    schedule 28.10.2015
comment
func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { if(receiptData == nil) { return false; } return (hasValidTrial(receiptData!) || isNotExpired(receiptData!)) && isNotCancelled(receiptData!) } Учитывая 3. есть ли лучший способ написать это? - person jenson-button-event; 09.03.2016
comment
Вы можете сказать func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { guard let nonNilReceiptData = receiptData else { return false} return (hasValidTrial(nonNilReceiptData) || isNotExpired(nonNilReceiptData)) && isNotCancelled(nonNilReceiptData) } - person rkgibson2; 31.01.2017
comment
Восклицательные знаки не обходятся без каких-либо проверок безопасности. Вы получите гарантированный сбой, если необязательный параметр равен нулю. Необязательный параметр отмечен всегда. Это просто очень жестокий способ проверки безопасности. - person gnasher729; 11.01.2018
comment
@ gnasher729 Как указано в ответе, он обходит проверки безопасности времени компиляции в пользу безопасного сбоя времени выполнения. - person Paul Cantrell; 12.01.2018

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

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

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

Другой способ проверить ненулевое значение (необязательное разворачивание)

    if let j = json {
        // do something with j
    }
person Fry    schedule 03.06.2014
comment
Да, я видел это в документации, но это все еще кажется ненужным ... поскольку, как мне кажется, john.apartment = number73 также говорит, что я знаю, что этот необязательный параметр определенно имеет значение; пожалуйста, используйте это .... - person Troy; 03.06.2014
comment
Да, это так, но дело в том, что по умолчанию Swift пытается перехватывать ошибки во время компиляции. Если вы знаете или думаете, что знаете, что эта переменная не может содержать nil, вы можете снять эту проверку с помощью восклицательного знака. - person lassej; 03.06.2014

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

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

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

word = name 

Здесь name имеет значение, поэтому мы можем его присвоить

var cow:String = nil
var dog:String!

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

dog = cow

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

person Ramkumar chintala    schedule 10.07.2015
comment
var c:Int = nil получит: Nil не может инициализировать указанный тип 'int' - person Asususer; 01.08.2017

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

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

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

person guest    schedule 24.07.2014
comment
Спасибо. Я также нашел следующую ссылку, которая объясняет это. Такой тип называется опцией с неявной развёрткой. stackoverflow.com/questions/24122601/ - person Kaydell; 06.09.2014

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

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

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

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

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

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

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

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

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

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

person Jim Driscoll    schedule 19.02.2016

! означает, что вы принудительно разворачиваете объект! следует. Дополнительную информацию можно найти в документации Apple, которую можно найти здесь: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html

person Henry oscannlain-miller    schedule 16.11.2014

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

Person? thisPerson;

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

thisPerson.Value
person Abdurrahman    schedule 20.10.2015

В объективном 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

 {..}
person Gokul    schedule 19.06.2014

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

person cheborneck    schedule 09.06.2015

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

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

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

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
person En Hui Lim    schedule 07.01.2016

John - необязательный элемент Person, то есть он может содержать значение или быть нулевым.

john.apartment = number73

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

john!.apartment = number73

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

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

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}
person Connor    schedule 03.06.2014
comment
Вы говорите, что если john равен нулю, а я иду john.apartment = number73, это НЕ приведет к ошибке, если я не поставлю '!' после Джона? - person Troy; 03.06.2014
comment
Если john является необязательным, вы должны использовать восклицательный знак для доступа к его свойствам, потому что компилятор должен знать, что это не nil. Если это не является обязательным, вы не можете использовать восклицательный знак. - person Connor; 03.06.2014
comment
Если я должен использовать восклицательный знак для ВСЕХ необязательных переменных, то почему Apple вообще заморачивался с этим, вместо того, чтобы сделать так, чтобы отсутствие восклицательного знака означало то же самое? Это похоже на ненужное раздувание ... Или есть случай, когда имеет смысл НЕ использовать восклицательный знак с необязательной переменной var? - person Troy; 03.06.2014
comment
вы используете восклицательный знак, когда вам нужно, чтобы необязательное значение не было равно нулю. Как и в случае с доступом к свойствам john. вы могли бы сделать что-то вроде var jack: Person? = john, где jack также является необязательным, или var jack: Person = john! где jack - это человек, а не ноль - person Connor; 03.06.2014
comment
Верно, но в исходном примере разве тот факт, что я разыменовываю необязательный параметр john, не говорит компилятору, что мне нужно, чтобы он не был равен нулю? ... И в вашем примере var jack: Person = john! разве отсутствие ? после того, как Person сообщит компилятору, что вам нужно john, чтобы оно не равнялось нулю? В целом, ! кажется излишним ... Мне все еще кажется, что я что-то упускаю в части "почему" в _5 _... - person Troy; 03.06.2014
comment
f john является необязательным, вы должны использовать восклицательный знак для доступа к его свойствам - на самом деле вам это не нужно. Снимите его и убедитесь сами - он все равно возвращает 123, как и ожидалось. - person Richard Washington; 07.06.2014
comment
Фактически, позже в документации будет рассказано о неявно развернутых опциях, используя приведенный ниже пример: “let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.” Но он отлично работает без!. Что-то здесь кажется странным. - person Richard Washington; 07.06.2014

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

var optionalExample: String?

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

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

person brimstone    schedule 26.02.2015

ПРОСТЫМИ СЛОВАМИ

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

person Maninderjit Singh    schedule 23.05.2016

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

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

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

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

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

person vishal dharankar    schedule 19.01.2017

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

if (myNumber != 3){
 // if myNumber is NOT 3 do whatever is inside these brackets.
)
person Andy Lebowitz    schedule 14.08.2017

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

случай 1: var myVar:String? = "Something"

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

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

в случае 2 он выйдет из строя.

Имея в виду ! mark заставит компилятор вернуть значение, даже если его нет. вот почему название Принудительное разворачивание.

person Ashish Pisey    schedule 06.09.2017
comment
@ Мориц. Я не знаю, так ли это? Не могу я ответить, если на вопрос уже дан ответ? Я вызываю какие-то проблемы или в моем ответе что-то не так? я не понимаю, почему вы отклоняете его за правильный ответ? Я попытался ответить, исходя из своего понимания концепции. Я думаю, что некоторым людям будет полезно ясное и простое объяснение. - person Ashish Pisey; 14.09.2017
comment
@Moritz Теперь это конструктивно. Вы должны были сказать мне об этом исправлении в первую очередь, если бы это было причиной. Спасибо за ответ. я исправил это. - person Ashish Pisey; 18.09.2017

Для сотрудников Google:

john!.department

... сообщает компилятору:

  • Я знаю, что john не является обязательным
  • Используйте его так, как будто он имеет ценность
  • Просто рухни, если нет

В производстве используйте guard let или if let, чтобы справиться с ситуацией обесценивания и аннулирования серьезных сбоев.

person theaws.blog    schedule 27.11.2020

СПРОСИТЕ СЕБЯ

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

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

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

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

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

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


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

person Honey    schedule 08.08.2016
comment
Использование настоящих слайдов выше, возможно, является нарушением авторских прав: ... Стэнфордский университет сохраняет авторские права на весь контент в нашей коллекции iTunes U. из http://itunes.stanford.edu. Наверное, лучше своими словами ответить на содержание курса, которое вы воспринимаете как ответ на этот вопрос (и вместо того, чтобы использовать слайды, скорее цитируйте соответствующие их части в кодовой форме, с ссылки, естественно). - person dfrib; 08.08.2016
comment
Сам @dfri Stanford бесплатно поделился своими слайдами в iTunes U. Я видел их слайды с несколькими ответами, и их слайды свободно распространялись в Интернете. Я много раз видел, как люди копируют / вставляют из блогов и приписывают им свои ссылки. Возможно, это первый раз, когда я вижу, как кто-то противостоит такому подходу. Я тоже к ним относился. В любом случае, спасибо за вашу заботу. - person Honey; 08.08.2016
comment
Ваш аргумент - argumentum ad populum. В любом случае, Я не верю, что Стэнфорд приедет сюда и обеспечит соблюдение прав DMCA, но всегда лучше, если ваши ответы будут изложены вашими собственными словами на основе официальных / достоверных источников, а не в форме прямого копирования этих источников, как указано выше; особенно, когда мы говорим о слайдах, защищенных авторским правом: опять же, лучше цитировать содержимое слайдов в блоках кода (изображения кода, как правило, здесь в SO!). - person dfrib; 08.08.2016
comment
Из присланной вами ссылки: С точки зрения авторских прав, я не думаю, что это правда. Вместо этого они должны удалить контент, нарушающий авторские права, по запросу правообладателя. Разница в том, что некоторые владельцы поощряют распространение своего контента таким образом, часто просто в надежде получить известность. - Я не хочу сказать, что вы ошибаетесь, скорее, я действительно не знаю, кто я делаете правильно или то, что вы говорите, правильно. - person Honey; 08.08.2016
comment
Мета-сообщение, на которое я ссылался, просто описывает подход SO: s к этому: сообщение, подобное этому, естественно, не будет удалено, если какой-то случайный пользователь сообщит об этом как о нарушении авторских прав: только если, например, представитель Стэнфорда. должны были прийти сюда, и, поскольку сообщение будет удалено, SO необходимо обеспечить его соблюдение. Поэтому я написал Я не верю, что Стэнфорд придет сюда и обеспечит соблюдение прав DMCA, .... Моя точка зрения выше заключается в том, что я считаю, что это, возможно, нарушение авторских прав (что я считаю неправильным), но, естественно, никто никогда не будет добиваться этого. ... - person dfrib; 08.08.2016
comment
Но помимо этого, размещение вопросов или ответов, содержащих изображения кода, категорически не рекомендуется по ряду причин. Подводя итог: этими комментариями я попытался указать, что, по моему мнению, этот ответ в его нынешней форме не совсем так хорош, как мог бы, по двум основным причинам (слайды, защищенные авторским правом, изображение кода). Естественно, никто, в том числе и я, ничего не будет делать с этим, я просто указываю на это для справки в будущем (или, возможно, призываю вас отредактировать этот пост с цитатой по блокам кода). Согласен, iTunes U! :) - person dfrib; 08.08.2016
comment
@dfri удаленные изображения - person Honey; 25.01.2017

person    schedule
comment
Пожалуйста, не пишите ответы, которые не добавляют ничего, что еще не было охвачено предыдущими ответами. - person Dalija Prasnikar; 25.10.2016