unwrap - tuples swift




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

Руководство по языку Swift Programming Language имеет следующий пример:

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


Что значит «развернуть экземпляр»? Почему это необходимо?

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

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

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

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

Обновление :

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

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

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

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

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

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

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

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

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


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


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


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

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

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

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

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

var John: Person!

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


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

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

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

word = name 

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

var cow:String = nil
var dog:String!

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

dog = cow

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


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

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

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

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

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


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

john.apartment = number73

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

john!.apartment = number73

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

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

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

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

Person? thisPerson;

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

thisPerson.Value

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

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

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

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

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

Каждая из них - это другая языковая конструкция с другим смыслом, но все они имеют три важные вещи:

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

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

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

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

2. Восклицательными знаками являются потенциальные сбои.

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

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

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

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

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

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

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

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

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

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

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

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

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


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

case 1: var myVar:String? = "Something" var myVar:String? = "Something"

case 2: var myVar:String? = nil var myVar:String? = nil

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

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

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


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

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

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

Другой способ проверить значение nil равно

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

В ПРОСТЫХ СЛОВАХ

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


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




forced-unwrapping