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

22 ответов


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

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

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

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

если есть значение, это может быть получено путем "разворачивания" переменной (получение T С Some(T)).

как john!.apartment = number73 отличается от john.apartment = number73? (Перефразирует)

если вы пишете имя необязательной переменной (например, text 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() (выполняет этот составленный метод, если значение существует)

обновление:

восклицательный знак также используется в синтаксисе для объявления " неявно развернуто Факультативный."

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

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

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

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

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

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

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

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


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

var john: Person?

означает, что Джон может быть нулевым

john?.apartment = number73

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

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

пока

john!.apartment = number73

компилятор будет интерпретировать эту строку как просто:

john.apartment = number73

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

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

обновление:

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


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


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

var john: Person?

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

остерегайтесь высокомерия восклицательного знака. Вместо этого ... --20-->

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

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

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

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

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

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

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

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

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


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

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

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

другой способ проверить значение без нуля -

    if let j = json {
        // do something with j
    }

вот несколько примеров:

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

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

word = name 

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

var cow:String = nil
var dog:String!

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

dog = cow

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


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

ВАР Джон: человек!

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


если вы пришли из языка семейства 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!)
}

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


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


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

Person? thisPerson;

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

thisPerson.Value

в цель с переменным без значения были равны 'нулю'(можно было использовать "нулевые" значения же 0 и false), поэтому можно было использовать переменные в условных операторах (переменные имеют значения такие же, как "истина" и неимеющих значения были равны в 'false').

Swift обеспечивает безопасность типа путем обеспечивать "опционное значение". т. е. он предотвращает ошибки, возникающие при назначении переменных разных типов.

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

var hw = "Hello World"

здесь, даже если "hw" является строкой, его нельзя использовать в операторе if, как в objective C.

//This is an error

if hw

 {..}

для этого его нужно создать как,

var nhw : String? = "Hello World"

//This is correct

if nhw

 {..}

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


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

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

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

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark

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

john.apartment = number73

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

john!.apartment = number73

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

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

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

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

var optionalExample: String?

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

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


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

использование восклицательного знака указывает, что переменная должна состоять не из нулевого значения (оно никогда не будет нулевым)


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

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

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

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

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


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

if (myNumber != 3){
 // if myNumber is NOT 3 do whatever is inside these brackets.
)

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

Пример 1: var myVar:String? = "Something"

Пример 2: var myVar:String? = nil

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

в случае 2 он рухнет.

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


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

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

  • тип person? есть apartment член/собственность? Или
  • тип person есть apartment член/собственность?

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

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

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

необязательный перечисление только 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? Ты ее видишь? Это не там вообще! Однако, если вы развернете его, ie do person! затем вы можете ... что он делает под капотом : Optional<Person>.Some(Person(name: "John Appleseed"))


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


как будущая дискуссия о том, почему использование ! развернуть иногда не рекомендуется видеть это Q & A