Что значит «развернуть экземпляр»? Зачем это нужно?
Насколько я могу потренироваться (это тоже очень ново для меня) ...
Термин «завернутый» подразумевает, что мы должны думать о необязательной переменной как о подарке, завернутом в блестящую бумагу, которая может (к сожалению!) Быть пустой .
Когда "обернуто", значение необязательной переменной является перечислением с двумя возможными значениями (немного как логическое значение). Это перечисление описывает, содержит ли переменная значение ( 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.
Вот в чем я думаю разница:
Значит джон может быть ноль
Компилятор интерпретирует эту строку как:
Пока
Компилятор интерпретирует эту строку просто:
Следовательно, используя! развернет оператор if и заставит его работать быстрее, но если john равен nil, произойдет ошибка времени выполнения.
Таким образом, перенос здесь не означает, что это память, но это означает, что это код, в этом случае он заключен в оператор if, а поскольку Apple уделяет пристальное внимание производительности во время выполнения, они хотят дать вам способ заставить ваше приложение работать с максимально возможной производительностью.
Обновить:
Возвращаясь к этому ответу через 4 года, так как я получил наибольшую репутацию от него в Stackoverflow :) Я немного не понял смысл распаковки в то время. Теперь, спустя 4 года, я считаю, что смысл развертывания здесь состоит в том, чтобы расширить код от его первоначальной компактной формы. Также это означает устранение неопределенности вокруг этого объекта, поскольку мы не уверены, что по определению он равен нулю или нет. Как и ответ Эшли выше, подумайте о нем как о подарке, в котором ничего не может быть. Но я все еще думаю, что развертывание - это развертывание кода, а не развертывание на основе памяти, как при использовании enum.
источник
TL; DR
Что означает восклицательный знак на языке Swift?
пример
Источник: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399
источник
let f: String! = "hello"
а затемprint(f)
, выводOptional("hello")
вместо просто"hello"
.Если бы john был необязательным var (объявленным так)
тогда Джон мог бы не иметь значения (на языке ObjC, значение ноль)
Восклицательный знак в основном говорит компилятору: «Я знаю, что это имеет значение, вам не нужно проверять его». Если вы не хотите использовать его, вы можете условно проверить его:
Интерьер этого будет только оценить, если Джон имеет значение.
источник
“let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.”
Но он отлично работает без! Здесь что-то странноеJohn?
иJohn
это два разных типа. Один имеет тип Необязательный Персона, а другой - Персона. Необязательное лицо должно быть развернуто, прежде чем вы сможете вывести человека. Но, как вы говорите, кажется, что это происходит, по крайней мере, в этих обстоятельствах без необходимости что-либо делать. Это, кажется, сделать! лишний. Если только ВСЕГДА необязателен, но рекомендуется делать, чтобы отловить ошибки времени компиляции. В некотором роде назначение vars / let для определенного типа может быть явным,let John: Person = ...
но также может быть выведеноlet John = ...
.Некоторая общая перспектива картины, чтобы добавить к другим полезным, но более детальным ответам:
В 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 { ... }
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) }
john
является необязательнымvar
. Так может быть содержитnil
значение. Чтобы убедиться, что значение не ноль, используйте!
в концеvar
имени.Из документации
«Если вы уверены, что необязательный параметр содержит значение, вы можете получить доступ к его базовому значению, добавив восклицательный знак (!) В конце имени необязательного элемента. Восклицательный знак фактически говорит: «Я знаю, что эта опция определенно имеет значение; пожалуйста, используйте это. ”
Еще один способ проверить ненулевое значение
источник
john.apartment = number73
также говорит: "Я знаю, что этот опционально определенно имеет значение; пожалуйста, используйте его." ...Вот некоторые примеры:
Где
word
это необязательное значение. означает, что может содержать или не содержать значение.Здесь
name
есть значение, чтобы мы могли назначить егоГде
dog
принудительно развернутый означает, что он должен содержать значениеПриложение будет зависать, потому что мы назначены
nil
для распаковкиисточник
var c:Int = nil
получит: «Nil не может инициализировать указанный тип« int »»В этом случае...
вар Джон: Человек!
это означает, что первоначально John будет иметь значение nil, оно будет установлено, и как только set никогда больше не будет иметь нулевое значение. Поэтому для удобства я могу использовать более простой синтаксис для доступа к необязательному var, потому что это «неявно развернутый необязательный»
источник
Если вы пришли из языка семейства C, вы будете думать «указатель на объект типа X, который может быть адресом памяти 0 (NULL)», и если вы исходите из языка с динамической типизацией, вы будете думая «Объект, который, вероятно, имеет тип X, но может быть неопределенного типа». Ничто из этого на самом деле не верно, хотя окольным путем первый близок.
То, как вы должны думать об этом, как будто это объект вроде:
Когда вы тестируете ваше необязательное значение,
foo == nil
оно действительно возвращаетсяfoo.isNil
, а когда вы говорите,foo!
оно возвращаетсяfoo.realObject
с утверждением, чтоfoo.isNil == false
. Важно отметить это, потому что если наfoo
самом деле ноль, когда вы делаетеfoo!
, то это ошибка времени выполнения, поэтому обычно вместо этого вы хотите использовать условное let, если только вы не уверены, что значение не будет nil. Такая хитрость означает, что язык может быть строго типизирован, не заставляя вас проверять, являются ли значения везде нулевыми.На практике это не так, потому что работа выполняется компилятором. На высоком уровне существует тип,
Foo?
который является отдельным от негоFoo
и который не позволяет функциям, принимающим тип,Foo
получать значение nil, но на низком уровне необязательное значение не является истинным объектом, поскольку у него нет свойств или методов; вполне вероятно, что на самом деле это указатель, который может быть равен NULL (0) с соответствующим тестом при принудительном развертывании.Есть другая ситуация, в которой вы увидите восклицательный знак типа:
Это примерно эквивалентно принятию необязательного с принудительной разверткой, то есть:
Вы можете использовать это, чтобы иметь метод, который технически принимает необязательное значение, но будет иметь ошибку времени выполнения, если он равен нулю. В текущей версии Swift это, по-видимому, обходит утверждение is-not-nil, поэтому вместо этого у вас будет ошибка низкого уровня. Обычно это не очень хорошая идея, но она может быть полезна при преобразовании кода с другого языка.
источник
! означает, что вы силой разворачиваете объект! следующим образом . Дополнительную информацию можно найти в документации Apple, которую можно найти здесь: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/TheBasics.html
источник
Если вы знакомы с C #, это похоже на типы Nullable, которые также объявляются с помощью знака вопроса:
И восклицательный знак в этом случае эквивалентен доступу к свойству .Value типа NULL, например:
источник
В целевых C переменные без значения были равны 'nil' (также можно было использовать значения 'nil', такие же, как 0 и false), следовательно, было возможно использовать переменные в условных выражениях (переменные со значениями такие же, как 'TRUE) 'и те, у которых нет значений, были равны' ЛОЖЬ ').
Swift обеспечивает безопасность типов, предоставляя «дополнительное значение». т.е. предотвращает возникновение ошибок при назначении переменных разных типов.
Таким образом, в Swift только условные выражения могут содержать только логические значения.
Здесь, даже несмотря на то, что «hw» является строкой, ее нельзя использовать в операторе if, как в цели C.
Для этого это должно быть создано как,
источник
! в конце объекта говорит, что объект является необязательным и развернуть, если он может в противном случае возвращает ноль. Это часто используется для перехвата ошибок, которые могли бы привести к сбою программы.
источник
Коротко (!): После того, как вы объявите переменную и убедитесь, что переменная содержит значение.
иначе вам придется делать это на каждом после передачи значения ...
источник
Джон - необязательный Персона, то есть он может иметь значение или быть нулем.
используется, если Джон не является обязательным Поскольку Джон никогда не равен нулю, мы можем быть уверены, что он не будет называть квартиру нулевой стоимостью. Пока
обещает компилятору, что john не nil, затем разворачивает необязательный параметр, чтобы получить значение john, и получает доступ к свойству квартиры john. Используйте это, если вы знаете, что Джон не ноль. Если вы называете это на ноль необязательно, вы получите ошибку во время выполнения.
Документация включает в себя хороший пример использования этого, где convertNumber является необязательным.
источник
john.apartment = number73
, это не приведет к ошибке, если я не поставлю '!' после джона?john
необязательный параметр, не говорит компилятору, что он должен быть ненулевым? ... А в вашемvar jack: Person = john!
примере, не отсутствие? после Person скажи компилятору, что нужноjohn
быть не ноль? В целом,!
кажется, что это кажется излишним ... Мне все еще кажется, что я что-то упускаю в части "почему"!
...Проще говоря, восклицательные знаки означают, что необязательный объект разворачивается. Необязательной является переменная, которая может иметь значение или нет - так что вы можете проверить, является ли переменная пустой, используя оператор if let, как показано здесь , и затем принудительно развернуть его. Если вы принудительно развернете необязательную опцию, которая пуста, ваша программа потерпит крах, так что будьте осторожны! Необязательные объявляются путем установки знака вопроса в конце явного присваивания переменной, например, я мог бы написать:
Эта переменная не имеет значения. Если бы я развернул его, программа вылетала бы, и Xcode сообщал бы вам, что вы пытались развернуть опциональный файл со значением nil.
Надеюсь, что это помогло.
источник
В простых словах
ИСПОЛЬЗОВАНИЕ Восклицательный знак указывает, что переменная должна состоять не из нулевого значения (оно никогда не должно быть ноль)
источник
Вся история начинается с функции swift, называемой необязательными переменными. Это переменные, которые могут иметь значение или не иметь значения. В целом, swift не позволяет нам использовать переменную, которая не инициализирована, так как это может привести к сбоям или неожиданным причинам, а также использовать сервер для заполнения бэкдоров. Таким образом, чтобы объявить переменную, значение которой изначально не определено, мы используем '?'. Когда такая переменная объявлена, чтобы использовать ее как часть некоторого выражения, необходимо развернуть их перед использованием, развертывание - это операция, посредством которой обнаруживается значение переменной, это относится к объектам. Без распаковки, если вы попытаетесь использовать их, у вас будет ошибка времени компиляции. Чтобы развернуть переменную, которая является необязательной переменной var, восклицательный знак "!" используется.
Теперь бывают случаи, когда вы знаете, что таким необязательным переменным будут присваиваться значения, например, системой или вашей собственной программой, но через некоторое время, например, выходами пользовательского интерфейса, в такой ситуации вместо объявления необязательной переменной с помощью вопросительного знака «?» мы используем "!".
При этом система знает, что эта переменная объявлена с "!" не является обязательным прямо сейчас и не имеет значения, но получит значение позже в течение своего срока службы.
Таким образом, восклицательный знак содержит два разных применения: 1. Объявить переменную, которая будет необязательной и получит значение определенно позже 2. Развернуть необязательную переменную перед ее использованием в выражении.
Надеюсь, вышеприведенные описания избегают слишком много технических вещей.
источник
Если вы используете его как необязательный, он развертывает необязательный и видит, есть ли что-то там. Если вы используете его в операторе if-else, это код для NOT. Например,
источник
Необязательная переменная может содержать значение или не
Дело 1:
var myVar:String? = "Something"
случай 2:
var myVar:String? = nil
теперь, если вы спросите myVar !, вы говорите компилятору вернуть значение в случае 1, он вернет
"Something"
в случае 2 произойдет сбой.
Смысл ! mark заставит компилятор возвращать значение, даже если его там нет. вот почему название Force Unwrapping .
источник
источник
СПРОСИ СЕБЯ
person?
естьapartment
член / свойство? ИЛИperson
естьapartment
член / свойство?Если вы не можете ответить на этот вопрос, тогда продолжайте читать:
Чтобы понять, вам может понадобиться супер базовый уровень понимания дженериков . Смотрите здесь . Многие вещи в Swift написаны с использованием Generics. Дополнительные включены
Код ниже был сделан доступным из этого Стэнфордского видео . Настоятельно рекомендую посмотреть первые 5 минут
Необязательным является enum только с 2 падежами
Опциональная привязка:
когда вы говорите,
var john: Person?
вы на самом деле имеете в виду такие:Имеет ли перечисленное выше перечисление какое-либо свойство с именем
apartment
? Вы видите это где-нибудь? Это не было вовсе! Однако, если вы развернете его, то есть сделаете,person!
вы сможете ... то, что он делает под капотом:Optional<Person>.Some(Person(name: "John Appleseed"))
Если бы вы определили
var john: Person
вместо:var john: Person?
тогда вам больше не нужно было бы!
использовать, потому чтоPerson
сам по себе имеет членapartment
В качестве дальнейшего обсуждения того, почему
!
иногда не рекомендуется использовать распаковку, посмотрите этот раздел вопросов и ответов.источник