Что означает восклицательный знак на языке 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 483

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

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

обновление:

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

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

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

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

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

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

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

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

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

вот в чем, по-моему, разница:

var john: Person?

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

john?.apartment = number73

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

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

пока

john!.apartment = number73

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

john.apartment = number73

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

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

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

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

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

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

в 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
  • исключения Nil-on-failure:try? NSJSONSerialization.JSONObjectWithData(data, [])

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

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

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

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

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

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

john является необязательным var. Так что можно содержащей 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 в развернутом

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

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

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

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

на практике он действительно не ведет себя так, потому что работа выполняется компилятором. На высоком уровне есть тип Foo?, которая является отдельной Foo, и это предотвращает функции, которые принимают тип Foo от получения нулевого значения, но на низком уровне необязательное значение не является истинным объектом, потому что у него нет свойств или методов; вполне вероятно, что на самом деле это указатель, который может по 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

в objective C переменные без значения были равны " nil "(также можно было использовать значения "nil", такие же как 0 и false), следовательно, можно было использовать переменные в условных операторах (переменные, имеющие значения, такие же, как "TRUE", а те, у которых нет значений, были равны "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!.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 как показано здесь, а затем заставить его развернуть. Если вы заставите развернуть дополнительный, который пуст, хотя, ваша программа рухнет, так что будьте осторожны! Варианты объявляются, поставив вопросительный знак в конце явного присвоения переменной, например, я мог бы напишите:

var optionalExample: String?

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

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

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

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

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

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

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

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


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